Search code examples
powershellparameter-passingparameter-sets

How to make one of two parameters mandatory, so that at least one of the two is always present?


mandatory would make both parameters required. I just need to make sure either path or fileList is always present.

I have been making do with the following but its not ideal:

function foo{
    Param(
    [string[]]$path
    [string[]]$fileList
    )
    if (($null -eq $path) -and ($fileList -eq "")){Write-Error -Message "Paths or FilieList must be used" -ErrorAction Stop}
}

win11/pwsh 7.4


Solution

  • Try the following:

    function foo {
      [CmdletBinding(DefaultParameterSetName = 'PathAndFileList')]
      Param(
    
        [Parameter(ParameterSetName='PathOnly', Mandatory)]
        [Parameter(ParameterSetName='PathAndFileList', Mandatory)]
        [string[]]$Path,
    
        [Parameter(ParameterSetName='FileListOnly', Mandatory)]
        [Parameter(ParameterSetName='PathAndFileList', Mandatory)]
        [string[]]$FileList
    
      )
      # Diagnostic output: Show which parameters were bound.
      $PSBoundParameters
    }
    

    The key is to use parameter sets:

    • One parameter-specific set each, for when only only one of the two parameters is bound on invocation (PathOnly, FileListOnly).

    • A shared one (PathAndFileList) for when both parameters are bound.

    • By marking both parameters as Mandatory in all parameter sets they belong to, argument-less invocations trigger an interactive prompt for providing value(s) to the mandatory parameter(s), or - in CLI sessions launched via the -NonInteractive parameter - trigger an error.

      • Via the DefaultParameterSetName property in the [CmdletBinding()] attribute you get to choose the default parameter set, which in interactive execution, in turn determines which parameter(s) are prompted for in argument-less invocations (or, generally speaking, in invocations where not all mandatory parameters in the target parameter set are bound).

      • Caveat: If you choose PathAndFileList as the default parameter set, both -Path and -FileList are prompted for in argument-less interactive calls, and must not be empty (by default, array-typed parameters do not accept an empty array), i.e. you must provide at least one value for both in order for the call to be made.
        While there is a [AllowEmptyCollection()] attribute, the problem is that if you add it to both parameters, you again enable an effectively argument-less invocation, if you enter nothing for / pass @() to both parameters.

      • That said, the UX of the interactive prompts is generally poor and limited with respect to what parameter types it supports.

        • See GitHub issue #4068 for a discussion, including a proposal to always throw an error in the absence of a mandatory parameter value (as with -NonInteractive), which would make this problem moot.