Powershell: Parameters for Scripts and (Advanced) Functions

Some notes on parameters for scripts or (advanced) functions in Powershell.

Parameter block for a script or an advanced function

At the beginning of the script, add CmdletBinding() and a Param() block.

Note that in most cases, this block must be the very first line of code in a script (comments and blank lines don’t matter), so don’t try to declare variables or define functions before it.

[CmdletBinding()]    # With that entry, a function turns into an 'advanced function'.

Param
(
    [Parameter(Mandatory=$True)]  [string] $StringParameter,
    [Parameter(Mandatory=$False)] [bool]   $BooleanParameter = $False,
    [Parameter(Mandatory=$False)] $IntegerParameter = 10,
    
    [Parameter(Mandatory=$False)]
    [switch]
    $SwitchParameter
)

CmdletBinding() is part of an “advanced functions/script cmdlet”. By that, a script or function automatically supports common parameters like -Verbose, -Debug, -WhatIf, -Confirm, -ErrorAction etc. Note that some of those common parameter still need additional support in the implementation to work right.

As usual, typing the variable (like [string] or [bool]) is not required, but can prevent some errors or weird behavior due casting issues.

Simple Function

For a normal function, parameters can also be provided simpler (and they are not required at all):

No parameters at all:

function Func
{
    # ...
}

Parameters in the function head

function Func ($Param1, [int] $Param2)
{
    # ...
}

Or parameters in the parameter block in the function body:

function Func
{
    param
    (
        $Param1,
        $Param2
    )
}

Validate parameter input

Example: Is the argument an existing file? ($_ is a shortcut for the value of the input argument.)

Param
(
    [ValidateScript({ Test-Path -Path $_ -PathType Leaf })]
    [string] $InputFile
)

Note that only incoming arguments are checked, not the default values!
That means, if you set the default value here to something like $InputFile = "C:\Some\Path\To\File.txt", and that path doesn’t exist, ValidateScript will not throw an error; you would need to test that later in the script explicitly.

Code Comment
[ValidateScript(<# See example above #>)] An expression that must return $true to be valid. See example above.
[ValidateRange(0,10)] The parameter must be between zero and ten.
[ValidateRange("Positive")] The parameter value must be greater than zero (enums are: “Positive”, “Negative”, “NonPositive”, “NonNegative”).
[ValidateCount(1,5)] The parameter takes one (minimum) to five (maximum) parameter values.
[ValidateLength(1,10)] The parameter value must have one (minimum) to ten (maximum) characters.
[ValidatePattern("[0-9][0-9][0-9][0-9]")] The parameter value must contain a four-digit number, and each digit must be a number zero to nine.
[ValidateSet("Low", "Medium", "High")] Only one of those three specified values will be accepted.
[ValidateNotNull()] The parameter value may not be null.
[ValidateNotNullOrEmpty()] The parameter value may not be null or empty.
[ValidateDrive("C", "D", "Variable", "Function")] The parameter value must be a PSDrive.
.. and some more…

And these are allowed for mandatory parameters:

See also https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_functions_advanced_parameters.

Parameter Set

Useful for mutually exclusive arguments (more at https://blog.simonw.se/powershell-functions-and-parameter-sets/).

Example: Parameter Param1 is always available for the combinations of sets that are specified in its [Parameter()] attributes(!), but either Enable_A or Enable_B can additionally and optionally be chosen:

function Set-Something
{
    [CmdletBinding(DefaultParameterSetName = "Default")]  # Optional
    param
    (
        [Parameter(Mandatory=$true)]
        [Parameter(ParameterSetName="Default")]
        [Parameter(ParameterSetName="Set_A")]
        [Parameter(ParameterSetName="Set_B")]
        [string] $Param1,
        
        [Parameter(ParameterSetName="Default")]
        [Parameter(ParameterSetName="Set_A")]
        [int] $Param2,

        [Parameter(ParameterSetName="Set_A")]
        [switch] $Enable_A,

        [Parameter(ParameterSetName="Set_B")]
        [switch] $Enable_B
    )

    # ...
}

Set-Something -Param1 "foo"
Set-Something -Param1 "foo" -Enable_A
Set-Something -Param1 "foo" -Enable_B
Set-Something -Param1 "foo" -Enable_A -Enable_B    # Will not autocomplete and will throw an error if you enforce it!
Set-Something -Param1 "foo" -Param2 100 -Enable_B  # Error, since Param2 doesn't list 'Set_B' in its [Parameter()] attribute!

Pass multiple values to a single parameter

By the way: Originally I used $input as the name for the parameter, but I soon discovered that it is a predefined Powershell variable in functions and script blocks; so, better use a different name.

function Func
{
    [CmdletBinding()]
    param
    (
        [string[]] $InputStr = @("String One", "String Two"')
    )

    foreach ($item in $InputStr) { Write-Output $item }
}

Func -InputStr "foo", "bar"

Some notes: