Adding Multiple Parameter Sets to a PowerShell Function

Sometimes you need to add more than one parameter set to a function you're creating. If that's not something you're familiar with, it can be a little confusing at first. In the following example, I want to either specify the Name or Module parameter, but not both at the same time. I also want the Path parameter to be available when using either of the parameter sets.

 1function Test-MrMultiParamSet {
 2    [CmdletBinding(DefaultParameterSetName='Name')]
 3    param (
 4        [Parameter(Mandatory,
 5                   ParameterSetName='Name')]
 6        [string[]]$Name,
 7
 8        [Parameter(Mandatory,
 9                   ParameterSetName='Module')]
10        [string[]]$Module,
11
12        [string]$Path
13    )
14    $PSCmdlet.ParameterSetName
15}

Taking a look at the syntax shows the function shown in the previous example does indeed have two different parameter sets and the Path parameter exists in both of them. The only problem is both the Name and Module parameters are mandatory and it would be nice to have Name available positionally.

1Get-Command -Name Test-MrMultiParamSet -Syntax
2Test-MrMultiParamSet -Name 'Testing Name Parameter Set' -Path C:\Demo\
3Test-MrMultiParamSet -Module 'Testing Name Parameter Set' -Path C:\Demo\
4Test-MrMultiParamSet 'Testing Name Parameter Set' -Path C:\Demo\

multi-param-sets1a.png

Simply specifying Name as being in position zero solves that problem.

 1function Test-MrMultiParamSet {
 2    [CmdletBinding(DefaultParameterSetName = 'Name')]
 3    param (
 4        [Parameter(Mandatory,
 5            ParameterSetName = 'Name',
 6            Position = 0)]
 7        [string[]]$Name,
 8
 9        [Parameter(Mandatory,
10            ParameterSetName = 'Module')]
11        [string[]]$Module,
12
13        [string]$Path
14    )
15    $PSCmdlet.ParameterSetName
16}

Notice that "Name" is now enclosed in square brackets when viewing the syntax for the function. This means that it's a positional parameter and specifying the parameter name is not required as long as its value is specified in the correct position. Keep in mind that you should always use full command and parameter names in any code that you share.

1Get-Command -Name Test-MrMultiParamSet -Syntax
2Test-MrMultiParamSet 'Testing Name Parameter Set' -Path C:\Demo\

multi-param-sets2a.png

While continuing to work on the parameters for this function, I decided to make the Path parameter available positionally as well as adding pipeline input support for it. I've seen others add those requirements similar to what's shown in the following example.

 1function Test-MrMultiParamSet {
 2    [CmdletBinding(DefaultParameterSetName = 'Name')]
 3    param (
 4        [Parameter(Mandatory,
 5            ParameterSetName = 'Name',
 6            Position = 0)]
 7        [string[]]$Name,
 8
 9        [Parameter(Mandatory,
10            ParameterSetName = 'Module')]
11        [string[]]$Module,
12
13        [Parameter(ParameterSetName = 'Name')]
14        [Parameter(ParameterSetName = 'Module')]
15        [Parameter(Mandatory,
16                   ValueFromPipeline,
17                   ValueFromPipelineByPropertyName,
18                   Position = 1)]
19        [string]$Path
20    )
21    $PSCmdlet.ParameterSetName
22}

This might initially seem to work, but what appears to happen is that it ignores the Parameter blocks for both the Name and Module parameter set names for the Path parameter because they are effectively blank. This is because another totally separate parameter block is specified for the Path parameter. Looking at the help for the Path parameter shows that it accepts pipeline input, but looking at the individual parameter sets seems to suggest that it doesn't. It's confused to say the least.

1'C:\Demo' | Test-MrMultiParamSet Test01
2help Test-MrMultiParamSet -Parameter Path
3(Get-Command -Name Test-MrMultiParamSet).ParameterSets.Parameters.Where({$_.Name -eq 'Path'})

multi-param-sets3a.png

There's honestly no reason to specify the individual parameter sets for the Path parameter if all of the options are going to be the same for all of the parameter sets.

 1function Test-MrMultiParamSet {
 2    [CmdletBinding(DefaultParameterSetName = 'Name')]
 3    param (
 4        [Parameter(Mandatory,
 5            ParameterSetName = 'Name',
 6            Position = 0)]
 7        [string[]]$Name,
 8
 9        [Parameter(Mandatory,
10            ParameterSetName = 'Module')]
11        [string[]]$Module,
12
13        [Parameter(Mandatory,
14                   ValueFromPipeline,
15                   ValueFromPipelineByPropertyName,
16                   Position = 1)]
17        [string]$Path
18    )
19    $PSCmdlet.ParameterSetName
20}

Removing those two empty parameter declarations above the Path parameter that reference the individual parameter sets clears up the problems.

1'C:\Demo' | Test-MrMultiParamSet Test01
2help Test-MrMultiParamSet -Parameter Path
3(Get-Command -Name Test-MrMultiParamSet).ParameterSets.Parameters.Where({$_.Name -eq 'Path'})

multi-param-sets5a.png

If you want to specify different options for the Path parameter to be used in different parameter sets, then you would need to explicitly specify those options as shown in the following example. To demonstrate this, I've omitted pipeline input by property name when the Module parameter set is used.

 1function Test-MrMultiParamSet {
 2    [CmdletBinding(DefaultParameterSetName = 'Name')]
 3    param (
 4        [Parameter(Mandatory,
 5            ParameterSetName = 'Name',
 6            Position = 0)]
 7        [string[]]$Name,
 8
 9        [Parameter(Mandatory,
10            ParameterSetName = 'Module')]
11        [string[]]$Module,
12
13        [Parameter(ParameterSetName = 'Name',
14                   Mandatory,
15                   ValueFromPipeline,
16                   ValueFromPipelineByPropertyName,
17                   Position = 1)]
18        [Parameter(ParameterSetName = 'Module',
19                   Mandatory,
20                   ValueFromPipeline,
21                   Position = 1)]
22        [string]$Path
23    )
24    $PSCmdlet.ParameterSetName
25}

Now everything looks correct.

1'C:\Demo' | Test-MrMultiParamSet Test01
2help Test-MrMultiParamSet -Parameter Path
3(Get-Command -Name Test-MrMultiParamSet).ParameterSets.Parameters.Where({$_.Name -eq 'Path'})

multi-param-sets4a.png

For more information about using multiple parameter sets in your functions, see the about_Functions_Advanced_Parameters help topic.

µ