How do set parameter dependencies

440 views Asked by At

Up till now I was more or less avoiding parameter-sets in my PowerShell script as I find them very verbose and quiet difficult to implement for complex dependencies.
There are several similar questions and answers at StackOverflow but I can find an acceptable solution or workaround to my situation. The actual script is even more complex but this is were I get stuck:

Function Test-ParamSet {
    [CmdletBinding(DefaultParameterSetName='Param1')][OutputType([Object[]])]Param (

        [Parameter(ParameterSetName = 'Param1', Mandatory = $True)]
        [Parameter(ParameterSetName = 'Param1Switch', Mandatory = $True)]
        $Param1,

        [Parameter(ParameterSetName = 'Param2', Mandatory = $True)]
        [Parameter(ParameterSetName = 'Param2Switch', Mandatory = $True)]
        $Param2,

        [Parameter(ParameterSetName = 'Param3', Mandatory = $True)]
        $Param3,

        [Parameter(ParameterSetName = 'Param1Switch')]
        [Parameter(ParameterSetName = 'Param2Switch')]
        [Switch]$Switch1,

        [Parameter(ParameterSetName = 'Param1Switch')]
        [Parameter(ParameterSetName = 'Param2Switch')]
        [Switch]$Switch2
    )
    Write-Host $PsCmdlet.ParameterSetName
}

Parameter rules:

  • At least one Param# (Param1, Param2 or Param3) parameter should be supplied
  • Only one Param# (Param1, Param2 or Param3) parameter can be supplied
  • The switches (Switch1 and Switch2) are optional can only be supplied (either one or both) with the Param1 or Param2 parameter

Meaning the following commands should produce an error:

Test-ParamSet
Test-ParamSet -Param3 'Test' -Switch1

And the following commands should be accepted:

Test-ParamSet -Param1 'Test'
Test-ParamSet -Param1 'Test' -Switch1
Test-ParamSet -Param2 'Test' -Switch1 -Switch2

The problem is with the following command:

Test-ParamSet -Param2 'Test'

It generates an unexpected error:

Test-ParamSet : Parameter set cannot be resolved using the specified named parameters. 
One or more parameters issued cannot be used together or an insufficient number of parameters were provided.
At line:1 char:1
+ Test-ParamSet -Param2 'Test'
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : InvalidArgument: (:) [Test-ParamSet], ParameterBindingException
+ FullyQualifiedErrorId : AmbiguousParameterSet,Test-ParamSet

I guess this is related to this github issue mentioned by @mklement0 in How do you change a parameter's throw message?.

But how can I adequately resolve or workaround this using parameter-sets?

2

There are 2 answers

1
Sage Pourpre On BEST ANSWER

The switch parameters should be member of the Param1 and Param2 naming set. Then, the Param1Switch & Param2Switch naming set can be removed from $Param1 & $Param2 definition.

Here's the end result.

Function Test-ParamSet {
    [CmdletBinding(DefaultParameterSetName='Param1')][OutputType([Object[]])]Param (

        [Parameter(ParameterSetName = 'Param1', Mandatory = $True)]
        $Param1,

        [Parameter(ParameterSetName = 'Param2', Mandatory = $True)]
        $Param2,

        [Parameter(ParameterSetName = 'Param3', Mandatory = $True)]
        $Param3,

        [Parameter(ParameterSetName = 'Param1')]
        [Parameter(ParameterSetName = 'Param2')]
        [Switch]$Switch1,

        [Parameter(ParameterSetName = 'Param1')]
        [Parameter(ParameterSetName = 'Param2')]
        [Switch]$Switch2
    )
    Write-Host $PsCmdlet.ParameterSetName
}
0
HAL9256 On

To add to @SagePourpre and @iRon comment and add what may be relevant extra information.

In this instance, the error is correct, and the issue is that you are matching 2 parameter sets at the same time --> with no default parameter set specified.

e.g. First parameter set test:

Test-ParamSet -Param1 'Test'

These match both Parameter sets Param1 and Param1Switch. It matches the other parameter set because the other two switches are optional. But, since you specify the DefaultParameterSetName='Param1' then Param1 wins by default.

In the second parameter set test:

Test-ParamSet -Param2 'Test'

Again, these match both Parameter sets Param2 and Param2Switch. But since there is no Default Parameter Set specified to "win" the disagreement, and the switches are optional (to match the second parameter set), it throws the error.

The solution is to simplify the set names so that it only can match one set at a time. Especially when you deal with optional parameters:

Function Test-ParamSet {
    [CmdletBinding(DefaultParameterSetName='Param1')][OutputType([Object[]])]Param (

        [Parameter(ParameterSetName = 'Param1', Mandatory = $True)]
        $Param1,

        [Parameter(ParameterSetName = 'Param2', Mandatory = $True)]
        $Param2,

        [Parameter(ParameterSetName = 'Param3', Mandatory = $True)]
        $Param3,

        [Parameter(ParameterSetName = 'Param1')]
        [Parameter(ParameterSetName = 'Param2')]
        [Switch]$Switch1,

        [Parameter(ParameterSetName = 'Param1')]
        [Parameter(ParameterSetName = 'Param2')]
        [Switch]$Switch2
    )
    Write-Host $PsCmdlet.ParameterSetName
}

The Other way of doing it is to specify the switches as mandatory to force the parameter set name to a specific one. e.g:

Function Test-ParamSet {
    [CmdletBinding(DefaultParameterSetName='Param1')][OutputType([Object[]])]Param (

        [Parameter(ParameterSetName = 'Param1', Mandatory = $True)]
        [Parameter(ParameterSetName = 'Param1Switch', Mandatory = $True)]
        $Param1,

        [Parameter(ParameterSetName = 'Param2', Mandatory = $True)]
        [Parameter(ParameterSetName = 'Param2Switch', Mandatory = $True)]
        $Param2,

        [Parameter(ParameterSetName = 'Param3', Mandatory = $True)]
        $Param3,

        [Parameter(ParameterSetName = 'Param1Switch', Mandatory = $True)]
        [Parameter(ParameterSetName = 'Param2Switch', Mandatory = $True)]
        [Parameter(ParameterSetName = 'Param1And2Switch', Mandatory = $True)]     
        [Switch]$Switch1,

        [Parameter(ParameterSetName = 'Param1Switch', Mandatory = $True)]
        [Parameter(ParameterSetName = 'Param2Switch', Mandatory = $True)]
        [Parameter(ParameterSetName = 'Param1And2Switch', Mandatory = $True)]     
        [Switch]$Switch2
    )
    Write-Host $PsCmdlet.ParameterSetName
}

This way you only have one ParameterSet option for all possible solutions, and can handle special cases with switches.