One of the coolest yet complex features of advanced functions in PowerShell is dynamic parameters and using that feature to perform PowerShell parameter validation. 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 Powershell parameter validation like building a PowerShell ValidateSet
array providing tab-completion on a parameter not based on a static set of stings but generated at runtime? These are both doable with dynamic parameters.
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, we also have:
- 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)
- 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.
There are a couple of 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 are 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 Powershell 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 PowerShell ValidateSet
array attribute.
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 MyParameter
parameter.
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.
[CmdletBinding()]
param()
DynamicParam {
}
Creating a Dynamic Powershell Parameter Validation the Hard Way
Inside the DynamicParam
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.
- First, instantiate a new
System.Management.Automation.RuntimeDefinedParameterDictionary
object to use as a container for the one or more parameters we’ll be adding to it using
$RuntimeParamDic = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
.
2. Next, create the System.Collections.ObjectModel.Collection
prepped to contain System.Attribute
objects.
$AttribColl = New-Object System.Collections.ObjectModel.Collection[System.Attribute]
.
3. Now 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.
$AttribColl.Add($ParamAttrib)
5. Because I’m using this dynamic parameter to build a PowerShell ValidateSet array for 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.
$RuntimeParamDic.Add('MyParameter', $RuntimeParam)
8. We can then return this runtime dictionary object back to the dynamic parameter block and we’re done!
return $RuntimeDic
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 creating the PowerShell ValidateSet array. 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.
Creating a Dynamic ValidateSet Array 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 MyParameter
parameter.
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!