The Active Directory PowerPack, the Quest AD cmdlets, and what can happen when you change public object interfaces or cmdlet parameter attributes in PowerShell

This is about what can happen when things go wrong and what not to do so that things don’t go wrong.

In case you didn’t know, I work at Quest Software on the PowerGUI Team.  My position is rather unique because I work exclusively with Windows PowerShell, and I don’t think there are too many jobs out there that offer that experience.  The main focus of my job is to create and extend PowerPacks, which are collections of PowerShell scripts that are packaged together to define an extension to the customizable PowerGUI Admin Console.  The scripts in the PowerPacks I create range anywhere from the extremely simple (one cmdlet or function call) to the very complicated (a script that calls into one or more functions that span many thousands of lines of PowerShell script).

Among others, one of the PowerPacks that I manage is the Active Directory PowerPack.  The Active Directory PowerPack provides a management interface to Active Directory, and it uses the Quest AD cmdlets in its PowerShell scripts to do the actual management of Active Directory.  For quite a few reasons1,  I need to explicitly indicate which connection should be used when you use that PowerPack to do something with Active Directory, regardless of what it is you are doing.  This means I am absolutely dependent on being able to determine what connection should be used in any script that is included in the PowerPack.  For scripts that are used at the beginning of a pipeline I look for the appropriate connection information that is stored globally.  For other scripts that are used later in a pipeline with Quest AD cmdlets I look at the Connection property that is attached to the objects coming down the pipeline.  This model has worked very well until recently when something unexpected happened.

Almost two weeks ago, just after version 1.2 of the Quest AD cmdlets was released, it came to my attention that the Connection property that I am so dependent on was not included on objects in that release.  For those of you who are not developers, this type of change (changing a public interface in an SDK) is very bad news.  It is called a breaking change, and the name is very fitting – the Active Directory PowerPack started breaking all over the place for users who upgraded their Quest AD cmdlets to version 1.2!

Amid a flurry of email discussions between myself and the Quest AD cmdlets team I spent the next week and a bit putting together an update to the Active Directory PowerPack so that it would work despite these changes, basically putting the Connection property there myself if it isn’t there.  At the same time, the Quest AD cmdlets team quickly started work on a 1.2.1 patch release to put the Connection property back in so that scripts outside of PowerGUI that use that property wouldn’t be affected either.  The patch for the Quest AD cmdlets is not available yet, but the updated Active Directory PowerPack, and the updated Network PowerPack which also used the Quest AD cmdlets a fair amount and was therefore susceptible to this change, are now posted in the PowerPack library and available for download.  If you use these PowerPacks, I strongly recommend you download and import the most recent releases of each of these PowerPacks.  Their document page in the PowerPack library contains all of the information you need to upgrade your PowerPacks and get started using the new versions.

Now that I have finished dealing with the impact of a breaking change like this one and the unpleasant work that ensues,  I thought it would be worthwhile to share with others what exactly consititutes a breaking change in a PowerShell cmdlet or advanced function.  The following is a list of 13 rules that you should adhere to when updating your cmdlets or advanced functions if you want to avoid introducing breaking changes:

  1. Do not remove any published parameters that are included in your published cmdlets or advanced functions.
  2. Do not rename any published parameters that are included in your published cmdlets or advanced functions unless you add an alias to the parameter that is the old parameter name.
  3. Do not reduce or remove pipeline support for any published parameters in your published cmdlets or advanced functions that support pipeline input.
  4. Do not remove support for $null input for any published parameters in your published cmdlets or advanced functions that support $null input.
  5. Do not modify the default value for any published parameters in your published cmdlets or advanced functions unless that modification will not change how the command functions in existing scripts.
  6. Do not add a required flag to any parameter in published parameter sets in your published cmdlets or advanced functions.
  7. Do not modify the position of any published parameters in published parameter sets in your published cmdlets or advanced functions.
  8. Do not replace the associated object type with an incompatible type for any published parameters in your published cmdlets or advanced functions.
  9. Do not remove wildcard support for any published parameters in your published cmdlets or advanced functions that have wildcard support.
  10. Do not remove support for building the value from the remaining arguments in any published parameters in your published cmdlets or advanced functions that support building the value in this manner.
  11. Do not remove any of your published parameters from published parameter sets in your published cmdlets or advanced functions.
  12. Do not increase validation restrictions for any of your published parameters in your published cmdlets or advanced functions that have validation restrictions.
  13. Do not modify or remove aliases for any published parameters in your published cmdlets or advanced functions.

Additionally you need to pay attention to the objects that are returned by your cmdlets or advanced functions so that you don’t introduce breaking changes on those either.  The following rules should be adhered to in order to avoid breaking changes on objects returned by your cmdlets or advanced functions:

  1. Do not remove a public property or method.
  2. Do not replace the types associated with a public property or method with incompatible types.
  3. Do not modify the order of the parameters in a public method.
  4. Do not add required parameters in a public method.

Not following these rules means risking breaking production scripts, risking making your customers unhappy, and many other potential undesirable consequences.  Please, if you are seriously trying to add value to the PowerShell ecosystem by extending it with additional commands, pay attention to these rules and don’t introduce breaking changes when you update them!

Thanks for listening!

Kirk out.

1 Reasons why the connection must be explicitly indicated in the Active Directory PowerPack:
1. The Quest AD cmdlets allow you to connect to many different directory services.
2. Opening a connection to a directory service with the Quest AD cmdlets changes a global variable that is used automatically by other cmdlets in the same snapin when you don’t explicitly indicate what connection to use.
3. In PowerGUI you can create or import other PowerPacks that may open their own connections to different directory services using the Quest AD cmdlets, Active Directory or otherwise.
4. In PowerShell, whether you are using the Quest AD cmdlets interactively or invoking a script that uses them, you are likely only working with one set of data from one directory service at a time in a logical manner.  Working in a user interface that is built on top of PowerShell is less restrictive, allowing you to move more freely between multiple data sets from multiple sources (directory services in this case) in what would be considered an illogical manner as far as script creation goes.

Share this post:

Advertisement

6 thoughts on “The Active Directory PowerPack, the Quest AD cmdlets, and what can happen when you change public object interfaces or cmdlet parameter attributes in PowerShell

  1. Generally it is agreed that you can introduce breaking changes for a major release (but not minor releases) as long as you make sure that users/customers are aware of the change(s). But you should seriously think about them.

  2. Thanks, Kirk. A great reference for scripting do’s and don’ts. And at this point, 8 years later, it’s pretty much a classic. I had a question about the recommendation to not modify parameter order in a public method/function. As I understand it, reordering parameters is only a problem if one is passing in params sans name, and to me, that’s a bad practice that should be discouraged. Comments? Thanks, Joel

    1. Hi Joel. So, this comment sat in my unapproved comment list and as I was cleaning up some spam I came across it accidentally. Apologies for the very, very late reply.

      You are correct, if you follow best practices and use named parameters, then reordering parameters won’t be a problem, for you. It will potentially be a problem for someone else, and the PowerShell community often doesn’t follow best practices (not entirely their fault — best practices have not really been very well communicated), so in general it is better to leave the order as is. That said, if the number of users using your function is relatively small, you may be able to deal with some potentially breaking changes quite easily.

      1. Hi, Kirk,
        Thanks for the reply. I hear what you’re saying, about not wanting to cause breaking changes for a large number of users, but that approach seems counter to encouraging best practices. Theoretically, I’d be in favor of giving people an ample heads-up that reordering will be happening, and then going ahead with the reorder. In a way, then, you’re rewarding those who follow best practice and educating those who don’t so that they hopefully start doing so. That said, I’m not sure I want to risk pissing off strangers that currently happily using my code. 🙂

      2. Well these days it’s less of an issue, as long as:
        – your customers are using a version of PowerShell that supports side-by-side versioning (in which case they can gradually update to a breaking change and modify scripts that use an old version to specifically use that version)
        – you use a versioning method that makes it easy to identify breaking changes, like Semantic Versioning (http://semver.org/)

        The only thing I don’t like about structure to support breaking changes is that some people think that makes it fair game and they make breaking changes all the time, which obviously, is less than desirable. 🙂

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s