Using Dynamic Parameters

Adding dynamic parameters to your function, module, script etc. can greatly increase its usability and user experience.

What is a dynamic paramter? It’s as the name suggests, it is a parameter that is dynamic, not static. Dynamic parameters also support tab completion, so it is similar to ValidateSet but again, it is dynamic, not a static list of options. See below for a few examples.

ValidateSet

function Example {
    [cmdletbinding()]
        param (
            [Parameter(Position = 0, Mandatory = $true)]
            [ValidateSet('1', '2', '3')]
            $Number
        )

        Write-Output $Number
    }

Dynamic Parameter (The hard way)

function DynamicParameter {
    [cmdletbinding()]
    param (
    )
    DynamicParam {
        # Set the dynamic parameters' name
        $ParamName_Name = 'Identity'
        # Create the collection of attributes
        $AttributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute]
        # Create and set the parameters' attributes
        $ParameterAttribute = New-Object System.Management.Automation.ParameterAttribute
        $ParameterAttribute.Mandatory = $true
        $ParameterAttribute.Position = 0
        # Add the attributes to the attributes collection
        $AttributeCollection.Add($ParameterAttribute)
        # Create the dictionary
        $RuntimeParameterDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
        # Generate and set the ValidateSet
        $arrSet = (Get-ChildItem -Path C:\temp\Example).Name
        $ValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute($arrSet)
        # Add the ValidateSet to the attributes collection
        $AttributeCollection.Add($ValidateSetAttribute)
        # Create and return the dynamic parameter
        $RuntimeParameter = New-Object System.Management.Automation.RuntimeDefinedParameter($ParamName_Name, [string], $AttributeCollection)
        $RuntimeParameterDictionary.Add($ParamName_Name, $RuntimeParameter)
        return $RuntimeParameterDictionary
    }
    Begin { }
    Process {
        $Identity = $PSBoundParameters[$ParamName_Name]

        Get-Content -Path C:\temp\Example\$Identity
    }
    End { }
}

Dynamic Parameter (The easy way)

function DynamicParameter {
    [cmdletbinding()]
    param (
        [Parameter(Position = 0)]$Identity
    )

    Get-Content -Path C:\Temp\Example\$Identity
}

$IdentityBlock = {
    param($CommandName, $ParameterName, $WordToComplete, $CommandAst, $FakeBoundParameter)

    (Get-ChildItem -Path C:\temp\Example).Name | Where-Object {
        $PSItem -like "$WordToComplete*"
    } | ForEach-Object {
        "'$PSItem'"
    }
}

Register-ArgumentCompleter -CommandName DynamicParameter -ParameterName Identity -ScriptBlock $IdentityBlock

Using Register-ArgumentCompleter offers multiple advantages over DynamicParam aside from just being easier to set up. It also allows you to properly set the positioning of the parameters and assign them to multiple sets.

Additionally a rule of thumb that I was told is if you are using a DynamicParam chances are you’re using the wrong tool. They do have a place but I have yet to find one.