The Surly Admin

Father, husband, IT Pro, cancer survivor

Parameter Sets

This has come up a couple of times and I’ve always managed to work around it, but there will come a time when you want to create an advanced function or script.  One with many parameters, and some of those parameters will be mutually exclusive.  You can do this using the ParameterSetName argument, inside the Parameter decorator.  Here’s how!

Search-User Script

As always, this come from building a script for someone on Spiceworks.  It started out fairly simple, OP needed a script that could search multiple domains for a particular user and if they’re locked with the option of unlocking them.  This was a bit of a challenge because none of my environments had multiple domains but one beauty of having a virtual environment in my test lab–my house–is that it’s pretty easy to spin something like this up.  So a couple of hours later the SubSurly domain was born.  Not too long after that I had a script that discovered the domains and attached to them using the built-in trusts and had a working script (see here for details).

Unfortunately, the OP didn’t have a nice trusted domain structure like mine, but instead had several independent domains–what were they thinking?!  I had an immediate idea to use my Get-SavedCredential function and figured I could modify things pretty quickly and easily to get what he wanted.  At right about that point I was pulled into my CFO’s office and told my job was being outsourced, and they wanted to keep me on until the end of January to help the outsourcing company transition into the role.  I won’t lie, there was a big portion of me that wanted to just walk away at that point–and not a few days in between–but I stuck it out and completed some projects and did all the knowledge transfer I could.  This didn’t leave any time to help the OP with his script and it sat idle for a couple of months.

Now my time at SeraCare has finished I find myself with plenty of time to work on the problem so I decided to dive in again and see what I could get going.  I created a simple CSV type here-string and used ConvertFrom-CSV to make objects out of it (a handy way of quickly creating a large array of objects).  Added the Get-SavedCredential function and had a working script.  Then it happened.

Scope Creep

Anytime you write a script, especially for someone else, it’s inevitable that the scope of the script will change, or creep.  I’ve written about it before and no matter how hard you try to avoid it, creep will still get you.  This often leads to trying to out think potential customers and add functionality you think they’ll probably ask for and this script was definitely a victim of that.  The original scope was simple:

  • Locate user(s) on multiple domains
  • Unlock the accounts

Which is fine, but I couldn’t help but wonder how long that would last.  How long before you want to enable/disable the user?  Or change their password?  I decided to expand the scope a bit:

  • Locate user(s) on multiple domains
  • Add ability to report even failed location attempts (just in case someone wants to know)
  • Input from pipeline as well as parameter
  • Support multiple administrators without changing code
  • Unlock, enable/disable and change password capability

Parameter Sets

The interesting part of the development of this script was as I was adding the enable/disable user account functionality.  I soon realized I could have both the Enable and Disable parameter on the same command line and it would work just fine!  Not earth shattering, of course, but a bit less than what we want to put out in the world.  Of course I could simple do some testing in the Begin{} scriptblock and this has some massive advantages in that I can much easier control the error message to the user.  But I really wanted to take advantage PowerShell’s ability to do this, even if the error message is a lot less than desired–to be honest the error message is horrible and confusing!

In the end, I had several parameters with essential 3 “sets”:

  • User, Password, Unlock, ShowAll, Path
  • User, ShowAll, Enable
  • User, ShowAll, Disable

One thing to keep in mind about Parameter Set’s is PowerShell has to be able to identify which set is which, so it can validate you have a good set (and not mixing and matching).  Enable and Disable were fine, since both of them were unique to their group.  But the top one was a problem.  While Password and Unlock were both unique, and everything worked fine if I used them, if I didn’t use them it was a problem.  And both parameters are optional, so if you just wanted to display a user suddenly the only parameter you have us User, which exists in all three sets.  This will throw an error because nothing is unique!

What to do?  This was almost a show stopper until I ran across an argument in the [CmdletBinding()] advanced function decorator.  Turns out there’s an argument called DefaultParameterSetName, which means if PowerShell can’t determine what parameter set to use then use the one specified with this argument.  So the set names I gave my 3 groups was User, Enable and Disabled.  So in my CmdletBinding decorator I put in DefaultParameterSetName=”User”.

Now I needed to define my parameter sets.  The argument we need here is ParameterSetName=”something”, so we’ll have to define “User”, “Enable” and “Disabled”.  The interesting thing, is since each set is defined individually, I need to set it for each parameter, and in the case of $User I had the Mandatory and ValueFromPipeline arguments too and since User was a member of all three parameter sets that meant I would have to repeat my bindings for each set!    Here’s what I mean:


[CmdletBinding(DefaultParameterSetName="User")]
Param (
[Parameter(Mandatory=$true,
ValueFromPipeline=$true,
ParameterSetName="User")]
[Parameter(Mandatory=$true,
ValueFromPipeline=$true,
ParameterSetName="Enabled")]
[Parameter(Mandatory=$true,
ValueFromPipeline=$true,
ParameterSetName="Disabled")]
[string[]]$User
)

view raw

ParamSet1.ps1

hosted with ❤ by GitHub

A bit wordy, as you can see, but in the end the User parameter is now a part of the User, Enable and Disable Parameter Sets.  Next come Password and Unlock, and since they are only a part of the User parameter set they’re a bit easier:


[Parameter(ParameterSetName="User")]
[switch]$Unlock,
[Parameter(ParameterSetName="User")]
[string]$Password,

view raw

ParamSet2.ps1

hosted with ❤ by GitHub

Next come the Enable and Disable parameters (and parameter sets):


[Parameter(ParameterSetName="Disabled")]
[switch]$Disable,
[Parameter(ParameterSetName="Enabled")]
[switch]$Enable,

view raw

ParamSet3.ps1

hosted with ❤ by GitHub

You should now see the pattern here of defining the parameters to their respective sets.  PowerShell will throw up an evil message when you try to cross the lines:

ParamSet1As you can see, the error message does leave a lot to be desired.  Maybe in version 5.0 they’ll give us a bit more control over the error’s thrown!  OK next two parameters are the ShowAll switch (which will show every domain that a user was searched in and that they weren’t found) and the Path switch.  Path is pretty critical as it’s where my CSV with the credential information will be retrieved as well as where the encrypted credential files will be stored.


[Parameter(ParameterSetName="User")]
[Parameter(ParameterSetName="Enabled")]
[Parameter(ParameterSetName="Disabled")]
[switch]$ShowAll,
[Parameter(ParameterSetName="User")]
[Parameter(ParameterSetName="Enabled")]
[Parameter(ParameterSetName="Disabled")]
[string]$Path

view raw

ParamSet4.ps1

hosted with ❤ by GitHub

Again, all three parameter set’s had to be repeated so the parameters would be available to all parameter sets.  This made the comment-based help section come out really nice as well after I wrote that up.  Check out the Syntax section:


SYNTAX
C:\dropbox\Scripts\PS Scripts\Search-User\Search-User.ps1 -User <String[]> [-Unlock] [-Password <String>]
[-ShowAll] [-Path <String>] [<CommonParameters>]
C:\dropbox\Scripts\PS Scripts\Search-User\Search-User.ps1 -User <String[]> [-Disable] [-ShowAll] [-Path <String>]
[<CommonParameters>]
C:\dropbox\Scripts\PS Scripts\Search-User\Search-User.ps1 -User <String[]> [-Enable] [-ShowAll] [-Path <String>]
[<CommonParameters>]

view raw

ParamSet.txt

hosted with ❤ by GitHub

So there you have it.  Parameter Set’s in a nutshell, I hope this will help you in writing your own advanced functions.  If you’re interested in the Search-User script you can find it on Spiceworks (where else?!)

Search-User.PS1

Oh, by the way!  This is my 100th blog post!  Only took, what, three years to get here?!

Advertisement

February 10, 2014 - Posted by | PowerShell | ,

1 Comment »

  1. Reblogged this on cambridgebiomed.

    Comment by cambridgebiomed | February 10, 2014 | Reply


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 )

Twitter picture

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

Facebook photo

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

Connecting to %s

%d bloggers like this: