Search code examples
powershellparametersdefault-value

Can I specify conditional default values for a parameter in PowerShell?


I thought if this was possible it might work using parameter sets so I tried the following:

Function New-TestMultipleDefaultValues {
    [CmdletBinding(DefaultParameterSetName="Default1")]
    param (
        [Parameter(Mandatory,ParameterSetName="Default1")]$SomeOtherThingThatIfSpecifiedShouldResultInTest1HavingValue1,
        [Parameter(ParameterSetName="Default1")]$Test1 = "Value1",
        [Parameter(ParameterSetName="Default2")]$Test1 = "Value2"
    )
    $PSBoundParameters
}

Executing this to create the function results in the error Duplicate parameter $test1 in parameter list. so it doesn't look like this way is an option.

The only thing I can think of at this point is to do something like this:

Function New-TestMultipleDefaultValues {
    param (
        $SomeOtherThingThatIfSpecifiedShouldResultInTest1HavingValue1,
        $Test1
    )
    if (-not $Test1 -and $SomeOtherThingThatIfSpecifiedShouldResultInTest1HavingValue1) {
        $Test1 = "Value1"
    } elseif (-not $Test1 -and -not $SomeOtherThingThatIfSpecifiedShouldResultInTest1HavingValue1) {
        $Test1 = "Value2"
    }

    $Test1
}

Which works but seems ugly:

PS C:\Users\user> New-TestMultipleDefaultValues -SomeOtherThingThatIfSpecifiedShouldResultInTest1HavingValue1 "thing"
Value1
PS C:\Users\user> New-TestMultipleDefaultValues
Value2
PS C:\Users\user> New-TestMultipleDefaultValues -Test1 "test"
test

Any better way to accomplish this?


Solution

  • The following should work:

    Since there is then no longer a need for explicit parameter sets, I've omitted them; without specific properties, the [Parameter()] attributes aren't strictly needed anymore either.

    Function New-TestMultipleDefaultValues {
    
      [CmdletBinding()]
      param (
        [Parameter()] $SomeOtherThing,
        [Parameter()] $Test1 = 
          ('Value2', 'Value1')[$PSBoundParameters.ContainsKey('SomeOtherThing')]
      )
        
      # * As expected, if -Test1 <value> is explicitly specified, 
      #   parameter variable $Test1 receives that value.
      # * If -Test1 is omitted, the expression assigns 'Value1` to $Test1
      #   if -SomeOtherThing was specified, and 'Value2' otherwise.
      $Test1 # Output the effective value of $Test1
    
    }
    
    • It is possible to use expressions as parameter default values.

      • The above code is an expression and therefore can be used as-is.
      • To use a single command (a call to a PowerShell cmdlet, function, script or to an external program) as an expression, enclose it in (...), the grouping operator.
      • In all other cases you need $(...), the subexpression operator (or @(...), the array-subexpression operator) to convert the code to an expression; these cases are:
        • A Throw statement (and, hypothetically, exit and return statements, but you wouldn't use them in this context)
        • A compound construct such as foreach, while, ...
        • Multiple commands, expressions, or compound constructs, separated with ;
    • However, it is safe to always use $(...) (or @(...)) to enclose the code that calculates the default value, which you may opt to do for simplicity.

    • These expressions are evaluated after the explicitly specified parameters have been bound, which allows an expression to examine what parameters have been bound, via the automatic $PSBoundParameters variable:

      • ('Value2', 'Value1')[$PSBoundParameters.ContainsKey('SomeOtherThing')] is simply a more concise reformulation of
        if ($PSBoundParameters.ContainsKey('SomeOtherThing')) { 'Value1' } else { 'Value2' }
        that takes advantage of [bool] values mapping onto 0 ($false) and 1 ($true) when used as an array index (integer).

      • In PowerShell v7+ you could use a ternary conditional instead, which has the added advantage of short-circuiting the evaluation: $PSBoundParameters.ContainsKey('SomeOtherThing') ? 'Value1' : 'Value2'