Learn PowerShell v2 features using PowerShell code snippets

Learning and using PowerShell v2 features just got easier!  Earlier this week I uploaded a collection of PowerShell v2 code snippets to the PowerGUI website, and they are ready for you to download and use.  All you need to do is follow the installation instructions in the snippet document.

Once you have the snippets installed, watch the screencast that demonstrates how you can learn more about some PowerShell v2 features by using the v2 code snippets.  You can watch it in flash format with a table of contents, or you can watch the YouTube version below.

Want to see more snippets?  Let me know which areas of PowerShell you would like to see covered in snippets by leaving me a note in the comments.

Enjoy!

Kirk out.

Share this post:

Recover deleted Active Directory objects with the AD Recycle Bin PowerPack

Last week Microsoft made the announcement that Windows Server 2008 R2 reached RTM.  Among the many cool new features provided with that release (Hello?  PowerShell v2?  Need I say more?), Microsoft has now added a recycle bin feature to Active Directory.  The management interface provided by Microsoft for this feature is the command line, or more specifically, PowerShell.  That’s great if you’re like me and you love to manage your infrastructure using PowerShell, but what if you prefer a GUI?  Fortunately there is a solution for you too.

As Jackson Shaw suggested on his blog about a week ago, PowerGUI provides an admin console that allows you to create your own management UI that is layered on top of PowerShell.  This admin console can be extended with PowerPacks, which are essentially add-ins that provide additional user interface elements in PowerGUI that invoke PowerShell script when clicked.  All you need to do is add the user interface elements you want and then provide the scripts to power those elements, managing the Active Directory Recycle Bin objects or anything else you need to manage.  Or alternatively you can check to see if someone on the PowerGUI Community like myself has already created a PowerPack with the functionality you are looking for.

In the case of the Active Directory Recycle Bin, you’re in luck.  I just finished creating the first release of a new PowerPack that is designed to allow you to manage any objects in your recycle bin.  You can find the Active Directory Recycle Bin PowerPack by following the hyperlink here or by going directly to http://www.powergui.org and browsing into the Active Directory subcategory in the PowerPack Library.  This PowerPack includes the following features:

  • View the contents of the recycle bin, including hierarchies
  • Restore individual items in the recycle bin (recursively or not) to their original location
  • Restore individual items in the recycle bin (recursively or not) to a specified location
  • Permanently delete objects in the recycle bin (recursively or not)
  • Empty the contents of the recycle bin
  • Modify the number of days that the recycle bin is configured to retain objects and the number of days that objects are to be kept in a tombstone state before permanent deletion

If you would like to see a demo of the Active Directory Recycle Bin PowerPack, watch this screencast:

If you prefer watching a high resolution version of the screencast, you can watch it in flash format here or on YouTube directly in HD format here.

This is the initial release of this PowerPack and it contains a good amount of new functionality.  If you are experimenting with the Active Directory Recycle Bin feature, please take a look at this PowerPack and provide any feedback you have so that we can continue to provide improvements that are valuable to you and others in future releases.

Thanks for listening!

Kirk out.

Share this post:

Essential PowerShell: Know your operator and enclosure precedence

PowerShell version 1 comes with a lot of operators, and the list becomes even longer in version 2 with cool new operators like -split and -join.  Whether you’re writing scripts or using PowerShell interactively, dealing with multiple operators in an expression that possibly contains different enclosures (brackets, quotation marks, etc.) as well can be very tricky.  It is very important to know how the PowerShell interpreter processes the expression so that you can get your expressions right the first time or, if you’re not so lucky, so that you can identify the problem in your expressions later and fix them. 

Recently there have been several posts on the forums where the problem has been a lack of understanding of the operator and enclosure precedence in PowerShell.  That’s not too surprising because the precedence order used by the PowerShell interpreter doesn’t seem to be documented at this time.  You can find the precedence order of arithmetic operators through the about_arithmetic_operators help file, you can find out some precedence details for specific operators in various operator help files, and you can find out the precedence of command types through the about_command_precedence help file, but that’s about it.  There is no single help file that documents the overall operator and enclosure precedence.  It doesn’t seem to be listed in any of the PowerShell books I have read either. 

Fortunately through some ad hoc experimentation and through some reading of the help documentation that does exist it is possible to figure out how all of this works.  I’ve gone through that exercise recently and the resulting table of operator and enclosure precedence is below.  Before getting to the table though there are a few important things I should mention, as follows: 

  1. Any items that share the same row in the table have the same precedence and are evaluated from left to right when adjacent within an expression unless otherwise indicated.
  2. The intent of this table is to identify a precedence that can be used to create or troubleshoot more complicated expressions without a lot of guesswork.  It is not intended to explain what each of the operators are and how you can use them (although to help understand expressions you might have to deal with I do mention a few details about some operators that function differently than the majority of operators in PowerShell).
  3. If you want to learn about the individual operators and see examples showing how they can be used I recommend you consult the about_operators help file and all of its related files.

With that out of the way, here is the operator and enclosure precedence table for PowerShell:

[] Type enclosures
Any character placed inside of these enclosures is treated as part of a literal type name.  The contents are not evaluated like an expression would be.
” “ Double-quoted string enclosures
‘ ‘ Single-quoted string enclosures
@” “@ Double-quoted here-string enclosures
@’ ‘@ Single-quoted here-string enclosures
 
{} Script block enclosures
 
() Nested expression enclosures
@() Array subexpression enclosures
$() Subexpression enclosures
 
. Property dereference operator
:: Static member operator
 
[] Index operator
 
[int]
[string[]]
etc.
Cast operators
Multiple adjacent operators in this row have a right-to-left evaluation.
-split (unary) Split operator (unary)
-join (unary) Join operator (unary)
These operators can be used as unary or binary operators.  Their precedence varies depending on how they are used.
, Comma operator
This operator is the array element separator.  It can be used as an unary or binary operator.
++ Increment operator
– – Decrement operator
These unary operators can be used before or after a variable or property.  When used before the variable or property (as a prefix operator), the value is incremented or decremented first and then the result is passed into the expression in which it is contained.  When used after the variable or property (as a postfix operator), the value is passed into the expression in which it is contained and then the variable or property is immediately incremented or decremented.
Negate operator
-not Not operator
! Not operator
-bnot Bitwise not operator
Multiple adjacent operators in this row have a right-to-left evaluation.
.. Range operator
 
-f Format operator
 
* Multiplication operator
/ Division operator
% Modulus operator
 
+ Addition operator
Subtraction operator
 
-split
-isplit
-csplit (binary)
Split operator (binary)
-join (binary) Join operator (binary)
-is Type is operator
-isnot Type is not operator
-as Type as operator
-eq
-ieq
-ceq
Equal to operator
-ne
-ine
-cne
Not equal to operator
-gt
-igt
-cgt
Greater than operator
-ge
-ige
-cge
Greater than or equal to operator
-lt
-ilt
-clt
Less than operator
-le
-ile
-cle
Less than or equal to operator
-like
-ilike
-clike
Like operator
-notlike
-inotlike
-cnotlike
Not like operator
-match
-imatch
-cmatch
Match operator
-notmatch
-inotmatch
-cnotmatch
Not match operator
-contains
-icontains
-ccontains
Contains operator
-notcontains
-inotcontains
-cnotcontains
Does not contain operator
-replace
-ireplace
-creplace
Replace operator
With the exception of the join operator and the type operators (-is, -isnot, and –as), each of the operators in this row has a case-sensitive and an explicit case-insensitive variant.  Case-sensitive variants are prefixed with c (e.g. -ceq) and case-insensitive variants are prefixed with i (e.g. –ireplace).
-band Bitwise and operator
-bor Bitwise or operator
-bxor Bitwise exclusive or operator
 
-and Logical and operator
-or Logical or operator
-xor Logical exclusive or operator
 
. Dot-sourcing operator
& Call operator
Unary operators that are only valid at the beginning of an expression, a nested expression, or a subexpression.
= Assignment operator
+= Assignment by addition operator
-= Assignment by subtraction operator
*= Assignment by multiplication operator
/= Assignment by division operator
%= Assignment by modulus operator
Multiple adjacent operators in this row have a right-to-left evaluation.

Since this table is created through experimentation and through snippets of information about precedence that I was able to find in the help files, it may not be entirely accurate.  If you find any problems with the precedence information provided here, please let me know and I’ll update this table accordingly. 

[Update 09-July-2009: Fixed table formatting, added an index operator, added all case-sensitive and case-insensitive variants and adjusted the precedence for the property dereference and static member operators.] 

Thanks, 

Kirk out. 

Share this post:

PowerShell Quick Tip: When you store an array, make sure it is an array

It is not uncommon to want to store the results of a command in an array so that you can refer to the contents of that array later in your script.  This is often done by simply assigning the results of a command to a variable.  For example, if you were importing the contents of a csv file into an array you might do it like this:

$collection = Import-Csv C:\newusers.csv

Commands like this will work forever as long as you have a csv file in the appropriate location.  Occasionally though you may find that a script containing this command just stops working.  It might not make sense to you at the time because the script hasn’t changed, so what’s the problem?

The problem is that in the above command there is no guarantee that $collection will actually store an array of objects because there is no guarantee that Import-Csv will return more than one object.  In fact, there is no guarantee that Import-Csv will return any objects at all.  If the C:\newusers.csv file is generated by some other tool, it is possible that it might contain 0, 1 or more entries.  When it contains 0 or 1 entries, the $collection variable will either be $null or it will contain one object representing the single entry in the C:\newusers.csv file, respectively.  If further down in your script you’re always treating $collection as if it is an array and using indices to access elements in the array, your script will suddenly start failing with errors.

Fortunately there is a way to ensure that the results of any command are stored in an array.  All you need to do is wrap the command that returns the object(s) in array enclosures, like this:

$collection = @(Import-Csv C:\newusers.csv)

If you take this approach in your script and then later when you run your script C:\newusers.csv only contains one entry or perhaps doesn’t contain any entries at all, your $collection variable will still be an array so that you can check the size using the Count property ($collection.Count), access individual values using indices (e.g. $collection[0]), etc.  This is a much safer practice to take when writing your scripts so that you can make sure that you have an array when you store an array.

Of course if you are dealing with very large collections of objects that aren’t already loaded in memory an even better practice is to avoid storing the entire collection in memory altogether by using pipelines and cmdlets like ForEach-Object to process the items in the collection, but I wanted to make sure you were aware of how you should properly handle and store arrays in variables in your scripts so that you’re not facing a script long after it has been written and asking yourself why it suddenly stopped working.  There are plenty of other reasons why a script could stop working at some point but at least now you’re armed with the knowledge required to avoid one of those possibilities.

Kirk out.

Share this post:

Create your own org chart from AD with PowerGUI!

Another week has gone by and I have another brand new PowerPack ready for download.  This time around it’s the Org Chart PowerPack.  This is a PowerPack that I put together based on a Get-OrgChart function I wrote to analyze org chart data at work.  It lets you do some really cool things such as:

  1. Dynamically create an org chart from users in Active Directory using title, department, office, address, and other properties.
  2. Generate a Visio Org Chart from PowerGUI for the any branch of an organization.
  3. Create statistical reports for the employees in your organization to see breakdowns of employees by office, department, management, etc.
  4. Dynamically generate Office Directory reports in HTML when using it in conjunction with the Advanced Reporting PowerPack.

Note: The Org Chart PowerPack uses the Quest AD cmdlets to retrieve information from AD so you will need to install those first before you can use the PowerPack.

If you would like to see how this PowerPack can be of benefit to you, check out this screencast:

This screencast was recorded in HD format so you can click on the HD button once you start watching it to enable high definition video.  Alternatively, if you would prefer to watch a high resolution flash version with a table of contents you can watch the screencast here.  I decided to try widescreen format this time since that is my format preference…let me know if this is a problem for you.

This is the first release of this PowerPack and I’m anxious to hear what you think so please give it a look and share your feedback so that I can improve it with another update in the future.

Thanks for listening!

Kirk out.

Share this post:

PowerShell Quick Tip: Setting AD object attributes with ScriptBlock parameters

In PowerShell you can pass pipeline data to parameters that are not configured to accept pipeline input using ScriptBlock parameters.  This has been discussed before and it is well worth your time to make yourself familiar with that capability of PowerShell because it allows you to create true one-liners in places where you might think you cannot.  Basically it boils down to this: you can pass a script block into parameters in a cmdlet that is not of type script block and within the script block you provide the $_ variable will contain the object that was just passed down the pipeline.  The script block will be evaluated first, and then the result will be passed into the cmdlet parameter.

Still, even with that knowledge in hand people often trip over the ScriptBlock parameter syntax when using a parameter that takes a hash table (aka an associative array) as input.  The most common example I can think of where this is encountered is in the Quest AD cmdlets.

Many of the Quest AD cmdlets have a parameter called ObjectAttributes.  This parameter serves two purposes: in Get cmdlets it is used to define the values you want to filter on; in Set and New cmdlets it is used to define the values you want to assign to specific attributes.  Not consistent, I know, but that’s a whole other discussion.  In both cases the ObjectAttributes parameter is a hash table and to use it you simply need to define a hash table that matches attribute names with values.  Here’s an example showing how users trip over the syntax without realizing it:

Get-QADComputer Comp1 `
    | Set-QADObject -ObjectAttributes ` 
        @{userAccountControl=$_.userAccountControl -bxor 2}

This one liner was designed to enable or disable a computer object in AD.  It will run without raising an error, and it will even enable or disable the computer object, but it will not work like you might expect.  After running this command the userAccountControl attribute (which contains many flags, not just a flag for enabled/disabled state) will not be properly configured.  Why?  It looks like it is properly using a ScriptBlock parameter, but it is not so the $_.userAccountControl value will either evaluate to 0 if the $_ variable is null or if it does not have a userAccountControl property, or it will contain the userAccountControl value from whatever object the $_ variable contained before you called Get-QADComputer in the first stage of this pipeline.  Nothing in this command instructs PowerShell to treat the ObjectAttributes parameter as a ScriptBlock parameter.  So what’s missing?

The most important thing to remember about ScriptBlock parameters is that they always must be surrounded by script block enclosures (“{“ and “}”).  Otherwise they are not script blocks.  In our example above it looks like the parameter is surrounded by the proper enclosures, but that’s not true.  It’s surrounded by hash table enclosures (“@{“ and “}”), and this is what trips people up when working with this syntax.  To make this be properly treated as a ScriptBlock parameter, we need to surround the hash table with curly braces, like this:

Get-QADComputer Comp1 `
    | Set-QADObject -ObjectAttributes ` 
        {@{userAccountControl=$_.userAccountControl -bxor 2}}

That makes PowerShell recognize that we have a ScriptBlock parameter and the $_ variable within it properly evaluates to the object that just came down the pipeline.  No ForEach-Object or temporary variables required.  It seems simple enough when explaining it but you’d be surprised how many people get tripped up on this syntax.

If you want to see some forum discussions where this has been an issue for others, you can go here or here.

Kirk out.

Share this post:

Just Released: Advanced Reporting PowerPack

I just published a brand new PowerPack to the PowerPack Library called the Advanced Reporting PowerPack.  If you’ve been keeping your eyes on the Virtualization EcoShell project as well as PowerGUI, you may have already come across this PowerPack because I released it there first.

The Advanced Reporting PowerPack allows you to generate HTML reports with collapsible nested groups for any set of data in PowerGUI.  Think VMs, Snapshots, AD Users or Groups…you name it.  If you can get the data into a grid in PowerGUI, you can generate a nice HTML report using this PowerPack.  The only UI element this PowerPack adds to PowerGUI is a common action called “Create report…”.  This action does all of the heavy lifting to generate a cool HTML report for the items you have selected.

Want to see what how to get started using this PowerPack?  Watch this screencast:

If you would prefer to watch a higher resolution version, you can watch the screencast in flash format here.

This is only the first release of advanced reporting functionality in PowerGUI and already it’s really powerful.  Still, there is room for improvement so if you have any feedback, please share it with me in my comments or on the PowerGUI Forums so that I can consider it for the next release!

Thanks and enjoy!

Kirk out.

Share this post:

Hanselminutes Show #162: PowerShell 2.0

While I was at tech·ed 2009 last week I ran into Hal while he was waiting for Scott Hanselman to finish up a game of Rock Band so that he could participate in a session for Scott’s popular podcast, HanselminutesHal asked me if I wanted to participate, to which I said absolutely yes, and the result is a 35 minute discussion with Hal, Scott and I about PowerShell 2.0.

Hal’s an IT pro, and while I work with IT pros a lot these days I have a developer background, so we were able to speak to both sides of the fence and talk about why you should pay attention to PowerShell whether you’re a software developer or an IT pro.

That recording is now live, so if you want to hear it head over to Hanselminutes Show #162: PowerShell 2.0, and let me know what you think afterwards.

Kirk out.

Share this post:

Create custom nodes easily with the Active Directory PowerPack and PowerGUI

Here’s a great question that someone just posted today on the PowerGUI forums:

I have the latest verions of the Quest AD cmdlets, PowerGUI and the Active Directory PowerPack.  Can I use PowerGUI to search for old computer accounts?  For example: computer accounts that haven’t had their machine account password reset for over three months?

The timing of this question is perfect because the update of the Active Directory PowerPack that I posted two weeks ago allows you to do this without having to write any scripts yourself.  For those of you who want to try this but don’t have the most recent versions of PowerGUI, the Quest AD cmdlets and the Active Directory PowerPack installed, follow those links and download and install those before continuing.  Installation instructions are on the appropriate web pages where necessary.

Once you all of the necessary software installed, here’s what you need to do.

Step 1: Add the attributes you need to the list of attributes retrieved from Active Directory.

There are In the PowerGUI Admin Console, expand the “Active Directory” node and click on “Configuration”.  This will show you the current domain that PowerGUI will gather data from.  You can change this if you like, however you don’t need to unless you want to use alternate credentials or connect to another domain.  With the configuration showing in the grid, click on the “Manage default attributes” link.  That will present you with a view something like this:

image

This shows you the current list of attributes that are retrieved for each individual Active Directory object.  This will be initially configured with the default attributes as defined by the Quest AD cmdlets plus a few attributes that are required for the Active Directory PowerPack.  You can add whatever attributes you want to gather here for any of the object types.  To help solve our immediate problem and be able to get a list of computer accounts that have not had their machine account password reset for over three months, we need to add the pwdLastSet attribute for computer objects.  To do this, simply click on the “Add attribute…” action, provide the name of the pwdLastSet attribute in the “Attribute name” field and set the “Retrieve attribute for computer objects” value to True.  When you click on OK, that attribute configuration will be saved and any computer objects retrieved using the Quest AD cmdlets will have that attribute retrieved with them.

Step 2: Retrieve the objects you want from Active Directory and optionally show the attributes you want to filter on.

Once you have configured PowerGUI so that it will retrieve the pwdLastSet attribute for computer objects, you need to get your computers.  You can do this by simply clicking on the “Computers” child node under the “Active Directory” node.  This will present you with a list of computer accounts, showing their Name, Type and DN by default.

With the list of computer objects showing, you can optionally add the pwdLastSet attribute to the view by right-clicking in the column headers and selecting “More…” from the menu that appears.  With the list of all attributes that can be added showing, scroll down until you find the pwdLastSet attribute and check the checkbox beside it.  Then click on OK to save that change and your pwdLastSet attribute will be retrieved with your computer objects from the Computers node.

Step 3: Create a client-side filter to filter out the objects you don’t care about.

With the list of all of your computer objects showing, you can create a filter to reduce the number of objects to those that you need to see.  To do this, click on the Filters button above the column headers.  This will make the filter panel visible.

image

You can use this panel to filter any collection based on the properties of the objects in that collection.  For our particular problem, we want to see computer objects whose password has not been changed in the last 3 months.  We can get that by selecting “pwdLastSet” in the “Property” field, “Less or equal” in the “Operator” field, and selecting the calendar date three months ago in the “Value” field using the calendar control.  Once that is set, click on the “Apply” button to apply the filter and your list of computer objects will be filtered to only show those whose machine passwords haven’t changed in the last 3 months.

Step 4: Make the newly filtered data set available in its own node.

Now that you have the data you’re looking after, your “Computers” node is now configured to always show you the filtered list because PowerGUI remembers the filters you apply to nodes.  You might not like this configuration, preferring to have a separate node to view the data that you want.  Fortunately, PowerGUI lets you do create that easily too, and you still don’t have to do any scripting.  To create a separate node that lets you get the same data set, simply click on the “Save As…” button in the filter panel.  This will allow you to save the script from the Computers node with the filter you have created automatically applied as a client-side filter for that script.  Simply give the new script node a name (something intuitive like “Computers with old passwords” will do), and click on OK to save the new child node.  Then you can click on the “Clear All” button in the filter panel for your “Computers” node because that filter will no longer be needed.  Now click on your new node and you’ll get the list of computers you are after, and it will look something like the screenshot below.

image

Step 5: Make your work reusable at any time by taking it one step further.

At this point you have successfully created a new node that gets specifically the data you were looking for and you didn’t have to do any scripting to get it, which is great!  You can make it a little better still though.  For our specific problem that we’re trying to solve, we created a new node that reports any computer object with a machine account password that hasn’t been changed within 3 months from today.  But tomorrow that will be come 3 months and 1 day.  The next day that will become 3 months and 2 days.  What if you want to customize the node so that it reports computers whose machine accounts have not had their passwords changed in the last 3 months, no matter what day you click on it?  For that to work you will have to do a minor script modification.

Right-click on your new “Computers with old passwords” node and select properties.  This will show you the script used to retrieve your computers and filter the list.  At the very end of that script, you will see something like the following:

func_Computers | Where-Object {
    $_.'pwdlastset' -le [System.DateTime]"2009-02-08"
}

The Where-Object cmdlet contains the filter that is excluding any computers whose pwdLastSet attribute is greater than 3 months from the day we did this (for me that that’s February 8, 2009 since I created the node on May 8, 2009).  To change that filter so that it always works using a date 3 months earlier than now, you simply need to replace [System.DateTime]”2009-02-08” with (Get-Date).AddMonths(-3).  That changes the end of our script to this:

func_Computers | Where-Object {
    $_.'pwdlastset' -le (Get-Date).AddMonths(-3)
}

That’s it!  A simple replacement of the filter condition and you’re off and running with a brand new node designed to meet your specific needs with PowerGUI, the Quest AD cmdlets and the Active Directory PowerPack!

In a future article I’ll show you just how simple it is for you to take a collection of extensions to PowerPacks that you have created like this and package them up in a PowerPack so that you can share them with other users in the Community as well!

Enjoy!

Kirk out.

 

Share this post:

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: