I came across this question on StackOverflow about throwing when a parameter is not supplied by pipeline . There have been many questions about this (without so much emphasis on the pipeline), so it seems to be a common request that PowerShell not prompt on a missing mandatory parameter, but instead throw an exception.

The short answer is that there is no way to do it if the parameter is in fact mandatory, so what we usually settle for is a simulation of required-ness that’s usually handled with a default value expression that throws an exception, like so:

function Get-Thing {
[CmdletBinding()]
param(
    $MyParam = $( throw 'MyParam is required' )
)
}

But this approach has its disadvantages.

Reasons Not to Do This

  • As alx9r points out this fails when the parameter is passed in via the pipeline.

  • It prevents the built-in PowerShell help from explaining that a parameter is mandatory in the usual way.

  • It can interfere with parameter binding if some parameter sets rely on the parameter being mandatory to be unambiguous.

  • It prevents automated discovery of mandatory parameters which can break automation and proxy functions.

Why to Do This

I’ve personally not needed this. I feel that the advantanges of marking a parameter (correctly) as mandatory far outweighed the slight annoyance I had when seeing that I forgot a mandatory parameter and then had to hit CTRL+C to supply it.

But I had a chat with the question asker and he explained that he has a lot of code that is deeply nested, sometimes 20+ levels, and it isn’t always obvious where in the stack a parameter is missing. I feel lucky that I have not had that experience, but I could see how it might suck up a lot of time. An exception would be more helpful. It could be caught and a breakpoint set so you could see what the current context is.

Workarounds

The last 3 items are unavoidable as long as we’re not making the parameter truly mandatory, so I’m going to focus on a solution that works with values passed by parameter or by pipeline.

Test it in the code

The obvious workaround is to test the parameter in the Process {} block:

Process {
    if (!$MyParam) {
        throw [System.ArgumentException]"MyParam is required"
    }
}

It works; it’s not very long, but it doesn’t scale. You need a separate conditional for every parameter in every function.

Wrap the test in a function

Another idea I had was to wrap the previous idea in a function:

function Validate-Parameter {
[CmdletBinding()]
param(
    [Parameter(
        Mandatory=$true , #irony
        ValueFromPipeline=$true
    )]
    [object]
    $o ,

    [String]
    $Message
)

    Begin {
        if (!$Message) {
            $Message = 'The specified parameter is required.'
        }
    }

    Process {
        if (!$o) {
            throw [System.ArgumentException]$Message
        }
    }
}

# Usage

Process {
    $a | Validate-Parameter -Message "-a is a required parameter"
    $a,$b,$c,$d | Validate-Parameter
}

I think it’s an improvement, but it still requires a lot of manual addition, especially if you want the message to be relevant.

One Test to Rule Them All

I got to thinking that if I could just get at the command info of the original function, I could parse the parameters and look for something else to use to decide whether or not to throw if a parameter that’s not actually mandatory is unbound. I decided on adding an [Alias()] attribute to each of these parameters that begings with Required_. Aliases can be seen in help (though not to extent that it makes up for the second bullet above), but they don’t show up by default, and they don’t pollute the tab completion, plus you can still add any other “real” aliases you want.

I’m including this as a gist so you can use git or copy/paste:

Validate-MandatoryOptionalParameters: Features and Usage

  • Same invocation for every situation (useful with ISE snippets), regardless of parameters.

  • Can set a breakpoint instead of throwing to aid in live debugging.

  • Use of -Debug switch provides further control.

  • Comment-based help included for parameter usage.

Test Invocation

function Test-Function {
[CmdletBinding()]
param(
    [Parameter(
        ValueFromPipeline=$true
    )]
    [Alias('Required_Param1')]
    $Param1 ,

    $Param2 ,

    [Alias('Required_Param3')]
    $Param3
)

    Process {
        $PSBoundParameters | Validate-MandatoryOptionalParameters -Context $MyInvocation.MyCommand
    }
}

The following calls all throw an exception:

Test-Function
Test-Function -Param1 'p1'
Test-Function -Param3 'p3'
'p1' | Test-Function

Or if Test-Function’s process block were this:

    Process {
        $PSBoundParameters | Validate-MandatoryOptionalParameters -Context $MyInvocation.MyCommand -SetBreakpoint
    }

It would set breakpoints which would be hit on the call to Test-Function that failed validation.

By adding -Debug:

    Process {
        $PSBoundParameters | Validate-MandatoryOptionalParameters -Context $MyInvocation.MyCommand -SetBreakpoint -Debug
    }

PowerShell will write the breakpoint info to the debug stream, and this also triggers PowerShell to confirm the action. That lets you break right then and there or let it just set the breakpoint and wait for the next time it hits.

And by the way if you ever need to get back to this page:

Get-Help Validate-MandatoryOptionalParameters -Online