PowerShell Parameter Sets: Enforcing Mandatory ComputerName OR IPAddress with Mutual Exclusion


9 views

When designing PowerShell functions with multiple connection methods, we often need to enforce these rules:

  • Exactly one connection identifier (ComputerName or IPAddress) must be specified
  • Credentials must be complete when provided (both username and password)

Here's the proper way to structure the parameter sets:

[CmdletBinding(DefaultParameterSetName="ComputerName")]
param (
    # ComputerName parameter set
    [Parameter(Mandatory=$true, ParameterSetName="ComputerName")]
    [Parameter(Mandatory=$true, ParameterSetName="ComputerNameCred")]
    [string]$ComputerName,

    # IPAddress parameter set
    [Parameter(Mandatory=$true, ParameterSetName="IPAddress")]
    [Parameter(Mandatory=$true, ParameterSetName="IPAddressCred")]
    [string]$IPAddress,

    # Credential parameters
    [Parameter(Mandatory=$true, ParameterSetName="ComputerNameCred")]
    [Parameter(Mandatory=$true, ParameterSetName="IPAddressCred")]
    [string]$AdminUser,

    [Parameter(Mandatory=$true, ParameterSetName="ComputerNameCred")]
    [Parameter(Mandatory=$true, ParameterSetName="IPAddressCred")]
    [SecureString]$AdminPassword
)

The key is creating distinct groups:

  • Base connection parameters (ComputerName/IPAddress only)
  • Credential variants (when authentication is needed)

Add this begin block to enforce mutual exclusion:

begin {
    if ($PSBoundParameters.ContainsKey('ComputerName') -and 
        $PSBoundParameters.ContainsKey('IPAddress')) {
        throw "Cannot specify both ComputerName and IPAddress parameters"
    }
    
    if (($PSBoundParameters.ContainsKey('AdminUser') -xor 
         $PSBoundParameters.ContainsKey('AdminPassword'))) {
        throw "Must specify both AdminUser and AdminPassword or neither"
    }
}

Valid invocations:

# ComputerName only
Connect-Server -ComputerName "SRV01"

# IPAddress only  
Connect-Server -IPAddress "192.168.1.100"

# With credentials
Connect-Server -ComputerName "SRV01" -AdminUser "admin" -AdminPassword (ConvertTo-SecureString "P@ssw0rd" -AsPlainText -Force)

Invalid invocations that will fail:

# Both identifiers (will throw)
Connect-Server -ComputerName "SRV01" -IPAddress "192.168.1.100"

# Missing password (will throw)
Connect-Server -ComputerName "SRV01" -AdminUser "admin"

For more complex scenarios, consider dynamic parameters:

dynamicparam {
    $runtimeParams = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
    
    if (!$PSBoundParameters.ContainsKey('ComputerName') -and 
        !$PSBoundParameters.ContainsKey('IPAddress')) {
        $attrib = New-Object System.Management.Automation.ParameterAttribute
        $attrib.Mandatory = $true
        $attrib.ParameterSetName = "DynamicComputerName"
        $attribColl = New-Object System.Collections.ObjectModel.Collection[System.Attribute]
        $attribColl.Add($attrib)
        $runtimeParams.Add("ComputerName", 
            New-Object System.Management.Automation.RuntimeDefinedParameter(
                "ComputerName", [string], $attribColl))
    }
    
    return $runtimeParams
}

When designing robust PowerShell functions, parameter sets provide a powerful way to create mutually exclusive parameter groups. However, configuring them to enforce complex validation rules can be tricky. Let's examine a complete solution for enforcing these requirements:

  1. Either ComputerName OR IPAddress must be specified (mandatory)
  2. These parameters should be mutually exclusive (only one can be used)
  3. If either AdminUser or AdminPassword is provided, both become mandatory

Here's the optimized version of the function with proper parameter sets:

function Connect-Target {
    [CmdletBinding(DefaultParameterSetName='ComputerName')]
    param (
        # Parameter set for computer name without credentials
        [Parameter(Mandatory=$true, ParameterSetName='ComputerName')]
        [Parameter(Mandatory=$true, ParameterSetName='ComputerNameWithCred')]
        [string]$ComputerName,

        # Parameter set for IP address without credentials
        [Parameter(Mandatory=$true, ParameterSetName='IPAddress')]
        [Parameter(Mandatory=$true, ParameterSetName='IPAddressWithCred')]
        [string]$IPAddress,

        # Credential parameters (mutually dependent)
        [Parameter(Mandatory=$true, ParameterSetName='ComputerNameWithCred')]
        [Parameter(Mandatory=$true, ParameterSetName='IPAddressWithCred')]
        [string]$AdminUser,

        [Parameter(Mandatory=$true, ParameterSetName='ComputerNameWithCred')]
        [Parameter(Mandatory=$true, ParameterSetName='IPAddressWithCred')]
        [securestring]$AdminPassword
    )

    # Validate that only one connection method is used
    if ($PSBoundParameters.ContainsKey('ComputerName') -and $PSBoundParameters.ContainsKey('IPAddress')) {
        throw "Cannot specify both ComputerName and IPAddress parameters"
    }

    # Rest of function implementation...
}

The solution uses these techniques:

  • Two primary parameter sets for connection methods (ComputerName/IPAddress)
  • Two secondary sets that include credential parameters
  • Explicit validation for mutual exclusion
  • Mandatory=$true on all primary parameters in their respective sets

Valid invocations:

# Using computer name without credentials
Connect-Target -ComputerName 'SERVER01'

# Using IP address with credentials
$securePass = ConvertTo-SecureString 'P@ssw0rd' -AsPlainText -Force
Connect-Target -IPAddress '192.168.1.100' -AdminUser 'admin' -AdminPassword $securePass

Invalid invocations that will be blocked:

# Missing required parameter (will prompt or fail)
Connect-Target

# Both connection methods specified (our custom validation catches this)
Connect-Target -ComputerName 'SERVER01' -IPAddress '192.168.1.100'

# Missing credential component
Connect-Target -ComputerName 'SERVER01' -AdminUser 'admin'

For better security practices, consider using [PSCredential] instead of separate username/password:

function Connect-Target {
    [CmdletBinding(DefaultParameterSetName='ComputerName')]
    param (
        [Parameter(Mandatory=$true, ParameterSetName='ComputerName')]
        [Parameter(Mandatory=$true, ParameterSetName='ComputerNameWithCred')]
        [string]$ComputerName,

        [Parameter(Mandatory=$true, ParameterSetName='IPAddress')]
        [Parameter(Mandatory=$true, ParameterSetName='IPAddressWithCred')]
        [string]$IPAddress,

        [Parameter(Mandatory=$true, ParameterSetName='ComputerNameWithCred')]
        [Parameter(Mandatory=$true, ParameterSetName='IPAddressWithCred')]
        [pscredential]$Credential
    )

    # Implementation would use $Credential.GetNetworkCredential()
    # for username/password when needed
}