Make Parameters Mandatory and Optional at the Same Time

by Jul 24, 2013

An often overlooked feature in PowerShell is the ability to make a function parameter both optional and mandatory – at the same time.

Let’s for example assume you want a function that has two parameters: ComputerName and Credential. Both should be optional, but if the user submits a Credential, then the parameter ComputerName must also be submitted. It needs to be either optional or mandatory, depending on the context used.

Here’s how you create a function:

function Get-ComputerInfo
{
    [CmdletBinding(DefaultParameterSetName='integrated')]
    param
    (
        [Parameter(Mandatory=$false,ParameterSetName='integrated')]
        [Parameter(Mandatory=$true,ParameterSetName='credential')]
        $ComputerName,

        [Parameter(ParameterSetName='credential')]
        $Credential
    )

    # code follows here
} 


The trick is to assign multiple “Parameter” statements to one parameter. In the example, the parameter ComputerName can be both mandatory and not mandatory, depending on the ParameterSet it is used in.

Since the parameter Credential is assigned to the ParameterSet “credential” (pick whatever name you want for ParameterSets), PowerShell now knows that in this context, ComputerName must be mandatory.

The initial CmdletBinding statement sets the default ParameterSet name, so if the user does not provide any parameter or just the (ambiguous) parameter ComputerName, then the ParameterSet is “integrated”.

So if you call Get-ComputerInfo with the parameter Credential, then PowerShell will automatically ask for ComputerName.

And here is a use case:

function Get-DiskInfo
{
    [CmdletBinding(DefaultParameterSetName='integrated')]
    param
    (
        [Parameter(Mandatory=$false,ParameterSetName='integrated')]
        [Parameter(Mandatory=$true,ParameterSetName='credential')]
        $ComputerName,

        [Parameter(ParameterSetName='credential')]
        $Credential
    )

    if ($PSBoundParameters.ContainsKey('Credential') -and $Credential -is [String])
    {
        $PSBoundParameters.Credential = Get-Credential -UserName $Credential -Message 'Enter Password'
    }

    Get-WmiObject -Class Win32_LogicalDisk -Filter 'DriveType=3' @PSBoundParameters -ea Stop
} 

Get-DiskInfo first checks whether the user has submitted a Credential, and if it is a string, it asks for the password. Then, the submitted parameters are forwarded to Get-WmiObject using splatting, retrieving information about all fixed disks.

You can use Get-DiskInfo now against your local machine or against any remote machine, optionally using credentials to authenticate, with a minimum of code.

Likewise, you can adapt this framework to thousands of other WMI-based queries and create your own set of network-aware tools.

ReTweet this Tip!