Parameter sets and splatting

This topic contains 5 replies, has 4 voices, and was last updated by Profile photo of Mike Hillis Mike Hillis 6 months, 1 week ago.

  • Author
    Posts
  • #41484
    Profile photo of Mike Hillis
    Mike Hillis
    Participant

    I'm having a problem making parameter sets work the way I want.

    My main function:

    function Get-ComputerName {
        [CmdletBinding(DefaultParameterSetName = 'AllComputers')]
        [OutputType([String])]
    
        Param(
            # Gets all computers: workstations, member servers, and domain controllers. This is the default behavior when no switch is specified.
            [Parameter(ParameterSetName="AllComputers")]
            [switch]$AllComputers,
    
            # Lists all workstations.
            [Parameter(ParameterSetName="SomeComputers")]
            [switch]$Workstations,
    
            # Lists all member servers.
            [Parameter(ParameterSetName="SomeComputers")]
            [Alias('Servers')]
            [switch]$MemberServers,
    
            # Lists all domain controllers.
            [Parameter(ParameterSetName="SomeComputers")]
            [Alias('DCs')]
            [switch]$DomainControllers,
    
            # Filters the list of computers to include only those currently online. May not be used with -Offline.
            [Parameter()]
            [switch]$Online,
    
            # Filters the list of computers to include only those currently offline. May not be used with -Online.
            [Parameter()]
            [switch]$Offline
        )
        
        BEGIN {}
        PROCESS{
            # Query AD. Clean up @PSBoundParameters and call Get-OnlineComputerName if needed.
        }
        END {}
    } #function Get-ComputerName 
    

    This function returns a list of computer names from Active Directory queries. If the -Online or -Offline parameters are supplied then this function calls Get-OnlineComputerName to test network connectivity:

    function Get-OnlineComputerName {
        [CmdletBinding(DefaultParameterSetName = 'Online')]
        [OutputType([String])]
        param (
            # Used to specify the computers of interest.
            [Parameter(Mandatory=$true,
                ValueFromPipeline=$true, 
                ValueFromPipelineByPropertyName=$true,
                Position=0)]
            [string[]]$ComputerName,
        
            # Returns a list of online computers.
            [Parameter(ParameterSetName="Online")]
            [switch]$Online,
    
            # Returns a list of offline computers.
            [Parameter(ParameterSetName="Offline")]
            [switch]$Offline
        )
    
        BEGIN {}
        PROCESS{
            # Return list of computers that passed or failed Test-Connection
        }
        END {}
    

    Help for Get-OnlineComputerName shows the desired syntax:
    Get-OnlineComputerName [-ComputerName] {string[]} [-Online] [{CommonParameters}]
    Get-OnlineComputerName [-ComputerName] {string[]} [-Offline] [{CommonParameters}]

    Help for Get-ComputerName shows this syntax:
    Get-ComputerName [-AllComputers] [-Online] [-Offline] [{CommonParameters}]
    Get-ComputerName [-Workstations] [-MemberServers] [DomainControllers] [-Online] [-Offline] [{CommonParameters}]

    I'd like to have Get-ComputerName accept -Online or -Offline, but not both, and to also have the option of using neither to get a computer name list without calling Get-OnlineComputerName to test connectivity. The closest I could get using parameter sets was to make -Online and -Offline mutually exclusive but the function then won't let me not use one of them.

    My options without parameters sets seem to be let Get-ComputerName pass both -Online and -Offline, at which point Get-OnlineComputername writes an error (not ideal since the user didn't call that function and doesn't know why it's giving an error) or use a Throw in Get-ComputerName when both -Online and -Offline have been used.

    Is there a cleaner approach?

    • This topic was modified 6 months, 1 week ago by Profile photo of Mike Hillis Mike Hillis.
  • #41491
    Profile photo of Don Jones
    Don Jones
    Keymaster

    So, I think I'd create a -Mode switch that accepted either Online or Offline, using a ValidateSet() to control that. Make the parameter non-mandatory, so it's also legal to not use it at all.

    Mutually exclusive multi-set switches are a bit hard. The way you've coded your first function, both -Online and -Offline are in all parameter sets. To do what you want, you'd have to create more parameter sets. One that takes -AllComputers and -Online, one that takes -AllComputers and -Offline, one that takes... you see where I'm going.

    Look at Where-Object. It has a zillion parameter sets for that exact reason. It's inelegant, but in that case it's how they hacked together the "easy syntax" option.

    • #41556
      Profile photo of Mike Hillis
      Mike Hillis
      Participant

      Solved: I went with the -Mode option, which prevents the -Online/-Offline conflict that was bothering me plus gives me tab completion for the -Mode arguments. Thanks for the help.

      My modified main function:

      function Get-ComputerName {
          [CmdletBinding(DefaultParameterSetName = 'AllComputers')]
          [OutputType([String])]
      
          Param(
              # Gets all computers: workstations, member servers, and domain controllers. This is the default behavior when no switch is specified.
              [Parameter(ParameterSetName='AllComputers')]
              [switch]$AllComputers,
      
              # Lists all workstations.
              [Parameter(ParameterSetName='SomeComputers')]
              [switch]$Workstations,
      
              # Lists all member servers.
              [Parameter(ParameterSetName='SomeComputers')]
              [Alias('Servers')]
              [switch]$MemberServers,
      
              # Lists all domain controllers.
              [Parameter(ParameterSetName='SomeComputers')]
              [Alias('DCs')]
              [switch]$DomainControllers,
      
              # Filters the list of computers to only those currently online or offline.
              [Parameter(Position=0)]
              [ValidateSet('Online','Offline')]
              [string]$Mode
          )
      

      My modified called function:

      function Get-OnlineComputerName {
          [CmdletBinding()]
          [OutputType([String])]
          param (
              # Specify the computers of interest.
              [Parameter(Mandatory=$true,
                  ValueFromPipeline=$true, 
                  ValueFromPipelineByPropertyName=$true,
                  Position=0)]
              [string[]]$ComputerName,
          
              # List either online or offline computers.
              [Parameter(Position=1)]
              [ValidateSet('Online','Offline')]
              [string]$Mode = 'Online'
          )
      

      Typical usage for Get-ComputerName:
      Get-ComputerName -Workstations Online

      There's a remaining minor issue. If the function includes comment based help, and my functions do, they the help syntax doesn't list the Online/Offline arguments for the -Mode parameter. If I leave off the comment based help then the auto-generated help syntax lists the Online/Offline arguments in the help syntax. Is there a way around that quirk to get the validate set arguments to list in the help syntax when using comment based help?

      • This reply was modified 6 months, 1 week ago by Profile photo of Mike Hillis Mike Hillis.
      • This reply was modified 6 months, 1 week ago by Profile photo of Mike Hillis Mike Hillis.
  • #41496
    Profile photo of Dave Wyatt
    Dave Wyatt
    Moderator

    In your Get-OnlineComputerName function, set a default parameter set of something other than 'Online' or 'Offline'. Doesn't matter what it's called. That'll tell the parameter binding engine how to behave if neither switch is used.

  • #41525
    Profile photo of Dan Potter
    Dan Potter
    Participant

    Where's the and splatting part? 🙂

    • #41539
      Profile photo of Mike Hillis
      Mike Hillis
      Participant

      Here's the splatting part:

              elseif ($Online -or $Offline) {
                  Write-Verbose "Sending computer name list to Get-OnlineComputerName"
                  $PSBoundParameters.Remove('AllComputers') | Out-Null
                  $PSBoundParameters.Remove('Workstations') | Out-Null
                  $PSBoundParameters.Remove('MemberServers') | Out-Null
                  $PSBoundParameters.Remove('DomainControllers') | Out-Null
                  Get-OnlineComputerName $FormattedList @PSBoundParameters
                  Write-Verbose "Finished call to Get-OnlineComputerName"
              } else {
      

You must be logged in to reply to this topic.