#PSBlogWeek – Dynamic Parameters and Parameter Validation
This post is part of the PowerShell Blogging Week (#PSBlogWeek) series on Windows PowerShell Advanced Functions, a series of coordinated posts designed to provide a comprehensive view of a particular topic.
In this series:
- Standard and Advanced PowerShell Functions by Francois-Xavier Cat (@LazyWinAdm) (March 30, 2015)
- PowerShell Advanced Functions: Can we build them better? With parameter validation, yes we can! byMike F. Robbins (@mikefrobbins) (March 31, 2015)
- #PSBloggingWeek – Dynamic Parameters and Parameter Validation by Adam Bertram (@adbertram)(April 1, 2015)
- Supporting WhatIf and Confirm in Advanced Functions by Jeffery Hicks (@JeffHicks) (April 2, 2015)
- Advanced Help for Advanced Functions by June Blender (@juneb_get_help) (April 3, 2015)
- A Look at Try/Catch in PowerShell by Boe Prox (@proxb) (April 4, 2015)
To suggest a PowerShell Blogging Week topic, leave a comment or tweet it to us with the #PSBlogWeek hashtag.
One of the coolest yet complex features of advanced functions in Powershell is dynamic parameters. Dynamic parameters take your typical function parameters to a whole new level. Have you ever had a time when you created an advanced function and wanted your parameters to depend on something else; to dynamically be created based on the criteria you choose at runtime? How about wanting parameter validation and tab-completion on a parameter not based on a static set of stings but generated at runtime? These are both doable with dynamic parameters.
There are a couple different ways to use dynamic parameters that I’ve seen. The first is the way that Ben Ten wrote about them on Powershell Magazine. Using this method, Ben was able to create parameters on the fly based on if a different parameter was used. Personally, I’ve never had a need to do this. I really like using dynamic parameters as a way to validate input based on some criteria that’s available at runtime. This way I can write a script that gathers information on-the-fly which allows me the beautiful parameter tab completion we all know and love. Let’s go over an example on how to create parameter validation based on files in a folder.
“Normal” advanced function parameters allow you to use a few
Validate options. You can validate the number of arguments a parameter can accept, the minimum and maximum length of a parameter argument, a set of options in an array, matching a regex string or a scriptblock and more. What I’m looking for here is to use the
ValidateSet attribute for the tab-completion.
You’ll notice in the example above I’m using the
Get-Item cmdlet and the default parameters for tab-completion which is to be expected. I want that functionality but I want to tab-complete my own arguments so let’s create a simple function to do that.
You’ll notice that I’ve highlighted the validation attribute that will allow us to tab-complete the
MyParameter argument. Now we’re able to get custom parameter argument tab-completion using the values specified in the
But now what if I want my tab-completion options to be generated on-the-fly based on some other criteria rather than a static list? The only option is to use dynamic parameters. In my example, I want to tab-complete a list of files in a particular folder at run-time. To get this done I’ll be using a dynamic parameter which will run
Get-ChildItem whenever I try to tab-complete the
With that being said, let’s make the
ValidateSet attribute of the
MyParameter parameter dynamic, shall we?
The first difference between a standard parameter and a dynamic parameter that you’ll notice is dynamic parameter are in their own block.
Creating a Dynamic Validation Parameter the Hard Way
Inside this block is where the magic happens and the magic does take a while to wrap your head around. A dynamic parameter is, in a sense, a
System.Management.Automation.RuntimeDefinedParameterDictionary object with one or more
System.Management.Automation.RuntimeDefinedParameter objects inside of it but it’s not quite that easy. Let’s break it down.
1. The first task is instantiating a new
System.Management.Automation.RuntimeDefinedParameterDictionary object to use as a container for the one or more parameters we’ll be adding to it.
$RuntimeParamDic = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
2. Next is creating the
System.Collections.ObjectModel.Collection prepped to contain
$AttribColl = New-Object System.Collections.ObjectModel.Collection[System.Attribute]
3. Instantiate a
System.Management.Automation.ParameterAttribute object which will hold all of the parameter attributes we’re used to. In our instance, I’m defining my parameter to be in all the parameter sets and accept pipeline input by a pipeline object and by property name.
$ParamAttrib = New-Object System.Management.Automation.ParameterAttribute
$ParamAttrib.Mandatory = $Mandatory.IsPresent
$ParamAttrib.ParameterSetName = '__AllParameterSets'
$ParamAttrib.ValueFromPipeline = $ValueFromPipeline.IsPresent
$ParamAttrib.ValueFromPipelineByPropertyName = $ValueFromPipelineByPropertyName.IsPresent
4. Add our parameter attribute set to the collection we instantiated above.
5. Because I’m using this dynamic parameter as a parameter validation I must also include a
System.Management.Automation.ValidateSetAttribute object inside of our attribute collection. This is where you define the code to actually create the values that allows us to tab-complete the parameter arguments.
$AttribColl.Add((New-Object System.Management.Automation.ValidateSetAttribute((Get-ChildItem C:\TheAwesome -File | Select-Object -ExpandProperty Name))))
6. We then have to instantiate a
System.Management.Automation.RuntimeDefinedParameter object using the parameter name, it’s type and the attribute collection we’ve been adding stuff to.
$RuntimeParam = New-Object System.Management.Automation.RuntimeDefinedParameter('MyParameter', [string], $AttribColl)
7. Once the run time parameter is finished we then come back to that original dictionary object we instantiated earlier using the parameter name and the runtime parameter object we created.
8. We can then return this runtime dictionary object back to the dynamic parameter block and we’re done!
Are your eyes glazing over yet? Mine was when I first tried to figure this out. Being the lazy admin I am I created a function called
New-ValidationDynamicParam that does all this work for you. Simply pass in the parameter name, the attributes you’d like the parameter to have and the code you’ll be using to create the validation and you’re done! The function does the rest. Isn’t this a lot easier than following steps 1-7?
Creating a Dynamic Validation Parameter the Easy Way
New-ValidationDynamicParam -Name 'MyParameter' -Mandatory -ValidateSetOptions (Get-ChildItem C:\TheAwesome -File | Select-Object -ExpandProperty Name)
My pain is your gain, people!
Now, with our dynamic validation parameter created, let’s take it for test drive.
I’ve got some files in a directory on my computer that I only want to be passed to the
Now all I have to do is run our script and voila! I’m now only able to use the file names as parameter arguments and they are updated as the files comes in and out of the folder!