Dynamic Parameter Validation
I want to continue exploring parameter validation with a new technique introduced in PowerShell 7. If you are still on Windows PowerShell, don't run away. I'll also share an alternative approach that works in both versions of PowerShell.
Traditional Validation with Sets
One of the parameter validations I covered recently is [ValidateSet()]
. This is an easy way to ensure the user enter's a value from a pre-defined set of values. Here is an example:
Function Measure-FileExtension {
[CmdletBinding()]
param(
[Parameter(HelpMessage = 'Enter the path to analyze')]
[string]$Path = '.',
[ValidateSet('ps1', 'ps1xml', 'txt', 'json', 'xml')]
[string]$Extension = 'ps1',
[switch]$Recurse
)
$stats = Get-ChildItem -File -Path $Path -Filter "*.$Extension" -Recurse:$Recurse |
Measure-Object -Property Length -Sum -Average -Maximum -Minimum
[PSCustomObject]@{
PSTypeName = 'FileExtensionStats'
Path = Convert-Path $Path
Extension = $Extension
Files = $stats.Count
TotalSize = $stats.Sum
Average = $stats.Average
}
}
I want to ensure that the user enters a valid file extension from the list. If they don't, PowerShell will throw an error. As a bonus, the user gets tab completion for the valid values.

The example is a complete and working function if you want to try it out.
You can use [ValidateSet()]
in Windows PowerShell and PowerShell 7. However, there is one limitation: the values are static. You must enter the values directly in the attribute. What if you want something more dynamic?
System.Management.Automation.IValidateSetValuesGenerator
This is where a new feature comes into the picture. You can define a PowerShell class that can dynamically populate the validation values. You will create a class that is inherited from System.Management.Automation.IValidateSetValuesGenerator
. This class has a GetValidValues()
method that will define the valid values for the parameter.
Here is an based on my previous example:
Class FileExtension : System.Management.Automation.IValidateSetValuesGenerator {
#there no class properties
#the GetValidValues method has no parameters
[string[]] GetValidValues() {
#the script block contains the code to generate the valid values
#I'm defining the array of valid values here
$FileExtension = @('ps1', 'ps1xml', 'txt', 'json', 'xml', 'yml', 'zip', 'md', 'csv')
#you must use the return keyword
return [string[]] $FileExtension
}
}
The name of my class is FileExtension
. The colon indicates that it inherits settings from the System.Management.Automation.IValidateSetValuesGenerator
class. The class has a single inherited method GetValidValues()
. The method will return an array of strings. The code in the script block is defining those values. This is not an enumeration, it is an object class with a method.
![The customn [FileExtension] class](https://assets.buttondown.email/images/109547cb-f67d-4411-81ed-6a432cd1282b.png?w=960&fit=max)
I can validate this in PowerShell.
PS C:\> $fe = [FileExtension]::new()
PS C:\> $fe.GetValidValues()
ps1
ps1xml
txt
json
xml
yml
zip
md
csv
If I want to change the values, I can revise the class and reload it into my session.
Using the Custom Class
To use the class, use it in the ValidateSet
attribute.
[ValidateSet([FileExtension])]
Here's the complete revised function.
Function Measure-FileExtension {
[CmdletBinding()]
param(
[Parameter(HelpMessage = 'Enter the path to analyze')]
[string]$Path = '.',
[ValidateSet([FileExtension])]
[string]$Extension = 'ps1',
[switch]$Recurse
)
$stats = Get-ChildItem -File -Path $Path -Filter "*.$Extension" -Recurse:$Recurse |
Measure-Object -Property Length -Sum -Average -Maximum -Minimum
[PSCustomObject]@{
PSTypeName = 'FileExtensionStats'
Path = Convert-Path $Path
Extension = $Extension
Files = $stats.Count
TotalSize = $stats.Sum
Average = $stats.Average
}
}
If you want to test, you'll need to make sure you have the FileExtension
class loaded in your session. You can copy and paste the class definition into your session or save it to a file and dot-source it.
I get the same tab completion as before, but this time the value are coming from the class.
