Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Making at least one of many parameters required in PowerShell

In PowerShell, I want to write a function, that accepts different options as parameters. It is OK, if it receives more than one parameter, but it has to receive at least one parameter. I want to enforce it through the parameter definition and not through code afterwards. I can get it to work with the following code:

function Set-Option {

    Param(
        [Parameter(Mandatory, ParameterSetName="AtLeastOption1")]
        [Parameter(Mandatory=$false, ParameterSetName="AtLeastOption2")]
        [Parameter(Mandatory=$false, ParameterSetName="AtLeastOption3")]
        $Option1,

        [Parameter(Mandatory=$false, ParameterSetName="AtLeastOption1")]
        [Parameter(Mandatory, ParameterSetName="AtLeastOption2")]
        [Parameter(Mandatory=$false, ParameterSetName="AtLeastOption3")]
        $Option2,

        [Parameter(Mandatory=$false, ParameterSetName="AtLeastOption1")]
        [Parameter(Mandatory=$false, ParameterSetName="AtLeastOption2")]
        [Parameter(Mandatory, ParameterSetName="AtLeastOption3")]
        $Option3
    )

    # Do stuff, but don't evaluate the plausibility of the given parameters here
}

But as you can see, it scales badly. For each additional option, I have to add a line to all other options. Can this be done in a more efficient and a more maintainable way?

As I already said, I don't want to check the parameters in the code, e. g. through evaluating $PSBoundParameters. I want it to happen in the parameter definition for auto-doc reasons.


If you need a real world example, have a look at Set-DhcpServerv4OptionValue which accepts many different options (-DnsDomain, -DnsServer, -Router, ...), where it is OK to have them all, but it makes no sense to have none.


Note: After several answers have already been provided, I just realized that my code is actually not working, if you provide more than one option.

like image 684
stackprotector Avatar asked Sep 06 '25 03:09

stackprotector


1 Answers

The following isn't a great solution - and depending on what you mean by auto-doc, it may not work for you - but it scales well, as you'll only ever need one additional parameter set:

function Set-Option {

  [CmdletBinding(DefaultParameterSetName='Fail')]
  Param(
      [Parameter(ParameterSetName='AtLeastOne')]
      $Option1,

      [Parameter(ParameterSetName='AtLeastOne')]
      $Option2,

      [Parameter(ParameterSetName='AtLeastOne')]
      $Option3,

      # Declare a dummy parameter whose default value throws an error
      # if no other parameter is passed.
      # Note: All that 'DontShow' does is to exclude the parameter
      #       from tab completion; doesn't hide it from syntax diagram.
      [Parameter(ParameterSetName='Fail', DontShow)] 
      ${-} = $(
        if ($PScmdlet.ParameterSetName -eq 'Fail') { 
          throw "Please specify at least one option." 
        }
      )
  )

  # Do stuff, without needing to evaluate the plausibility of
  # the given parameters here.

}
  • All real parameters are optional and belong to the same parameter set that is not the default.

  • The purpose of dummy parameter ${-}, which is the only one in the default parameter set, is solely to throw an error via its default value.

    • Since the default value is always evaluated, a conditional is needed to throw only if the default parameter set has been selected ($PScmdlet.ParameterSetName -eq 'Fail').

    • Due to the parameter's irregular name, you actually cannot pass an explicit value to it (which is desirable here, because it is purely auxiliary and not meant for direct use): you'd have to use -- <value>, but -- has special meaning to the parameter binder (deactivates named parameter binding for the subsequent arguments).

    • Unfortunately, property DontShow (e.g. [Parameter(DontShow)]) only hides the parameter from tab-completion, not also from the syntax diagrams.

      • GitHub issue #7868 proposes introducing a way to hide (obsolete) parameters from the syntax diagram.

Thus, unfortunately, the dummy parameter set and its parameter appear in the syntax diagram, so that Set-Option -? shows the following:

SYNTAX
    Set-Option [-- <Object>] [<CommonParameters>]

    Set-Option [-Option1 <Object>] [-Option2 <Object>] [-Option3 <Object>] [<CommonParameters>]

Note that syntax diagrams lack a notation for your desired logic.

like image 83
mklement0 Avatar answered Sep 08 '25 12:09

mklement0