PowerShell Advanced Functions: Can we build them better? With parameter validation, yes we can!

This blog article is the second in a series of blog articles that is being written on PowerShell advanced functions for PowerShell blogging week. If you haven’t already read the first article in this series: Standard and Advanced PowerShell functions written by PowerShell MVP Francois-Xavier Cat I recommend reading it also.

What is parameter validation? In PowerShell, parameter validation is the automated testing to validate the accuracy of parameter values passed to a command.

Why validate parameter input? The question should be, can your function complete successfully without valid input being provided? If not, parameter validation should be performed to catch problems early on and before your function performs any actions. There could also be security risks associated with accepting input that isn’t validated.

In this first example, no parameter validation is being performed:

function Test-NoValidation {
    [CmdletBinding()]
    param (
        $FileName
    )
    Write-Output $FileName
}

This allows any number of values and any value including null, empty, or invalid file names to be provided for the FileName parameter:

param-validate1a.jpg

There are several different parameter validation attributes that can be used to validate the values that are provided for parameter input.

ValidateLength is one of those attributes. It validates that the number of characters are within a specified range as shown in the following example where the value provided for the FileName parameter must be between one and twelve characters in length:

function Test-ValidateLength {
    [CmdletBinding()]
    param (
        [ValidateLength(1,12)]
        [string]$FileName
    )
    Write-Output "$FileName is $($FileName.Length) characters long"
}

Typing the FileName variable as a [string] prevents more than one value from being provided for it as shown in the previous example.

Values outside the specified character length generate an error:

param-validate2a.jpg

ValidateLength probably isn’t the best parameter validation attribute for validating something like a file name since it allows invalid file name characters to be specified.

ValidatePattern validates the input against a regular expression:

function Test-ValidatePattern {
    [CmdletBinding()]
    param (
        [ValidatePattern('^(?!^(PRN|AUX|CLOCK\$|NUL|CON|COM\d|LPT\d|\..*)(\..+)?$)[^\x00-\x1f\\?*:\"";|/]+$')]
        [string]$FileName
    )
    Write-Output $FileName
}

If the value doesn’t match the regular expression, an error is generated:

param-validate3a.jpg

As you can see in the previous example, the error messages that ValidatePattern generates are cryptic unless you read regular expressions and since most people don’t, I typically avoid using it. The same type of input validation can be performed using ValidateScript while providing the user of your function with a meaningful error message.

ValidateScript uses a script to validate the value:

function Test-ValidateScript {
    [CmdletBinding()]
    param (
        [ValidateScript({
            If ($_ -match '^(?!^(PRN|AUX|CLOCK\$|NUL|CON|COM\d|LPT\d|\..*)(\..+)?$)[^\x00-\x1f\\?*:\"";|/]+$') {
                $True
            }
            else {
                Throw "$_ is either not a valid filename or it is not recommended."
            }
        })]
        [string]$FileName
    )
    Write-Output $FileName
}

Notice the meaningful error message:

param-validate4a.jpg

ValidateCount limits the number of values that can be provided:

function Test-ValidateCount {
    [CmdletBinding()]
    param (
        [ValidateCount(2,6)]
        [string[]]$ComputerName
    )
    Write-Output "The ComputerName array contains $($ComputerName.Count) items."
}

Typing the variable as a [string[]] allows multiple values to be provided.

Specifying too few or too many values generates an error:

param-validate6a.jpg

ValidateRange verifies the value is within a specific numeric range:

function Test-ValidateRange {
    [CmdletBinding()]
    param (
        [ValidateRange(1582,9999)]
        [int]$Year
    )
    Write-Output "$Year is a valid Gregorian calendar year"
}

Verify the input is between 1582 and 9999:

param-validate7b.jpg

ValidateSet specifies a specific set of valid values:

function Test-ValidateSet {
    [CmdletBinding()]
    param (
        [ValidateSet('CurrentUser','LocalMachine')]
        [string]$StoreLocation
    )
    Write-Output $StoreLocation
}

Beginning with PowerShell version 3, those values will tab expand in the PowerShell console and they’ll show up in intellisense in the PowerShell ISE (Integrated Scripting Environment) and most third party products such as SAPIEN PowerShell Studio.

param-validate8a.jpg

In the previous examples, the parameters weren’t designated as being mandatory however. This means that they aren’t required to be specified:

param-validate9a.jpg

Mandatory parameters require the user to provide a value:

#Requires -Version 3.0
#Requires -Version 3.0
function Test-ValidateSet {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]
        [ValidateSet('CurrentUser','LocalMachine')]
        [string]$StoreLocation
    )
    Write-Output $StoreLocation
}

If a mandatory parameter isn’t specified, you’re prompted for a value:

param-validate10a.jpg

Default values can’t be used with mandatory parameters. If a default value is specified with a mandatory parameter and the parameter isn’t specified when calling the function, you’ll still be prompted for a value (the default value will never be used).

ValidateNotNullOrEmpty prevents null or empty values from being provided and default values can be used with this particular validation attribute:

function Test-NotNullOrEmpty {
    [CmdletBinding()]
    param (
        [ValidateNotNullOrEmpty()]
        [string]$ComputerName = $env:COMPUTERNAME
    )
    Write-Output $ComputerName
}

The default value is used when the parameter isn’t specified:

param-validate11a.jpg

I’ve demonstrated the more common parameter validation attributes in this blog article, to learn more see the about_Functions_Advanced_Parameters help topic:

help about_Functions_Advanced_Parameters -ShowWindow

Be sure to keep an eye on the #PSBlogWeek hash tag on twitter and the PSBlogWeek twitter list that I’ve created to know when each of the additional articles in this series of blog articles are published throughout the remainder of the week.

Also, be sure to take a look at my blog article The mother of all PowerShell blogs week is coming next week! that I published last week to learn who the bloggers are that are participating in PowerShell blogging week.


Update - April 5, 2015:

Yesterday was the last day in the PowerShell blogging week series so I thought I would add direct links to all of the articles since it has now concluded.

Name PSBlogWeek Article
Francois-Xavier Cat Standard and Advanced PowerShell functions
Mike F Robbins PowerShell Advanced Functions: Can we build them better? With parameter validation, yes we can!
Adam Bertram #PSBlogWeek – Dynamic Parameters and Parameter Validation
Jeffery Hicks PowerShell Blogging Week: Supporting WhatIf and Confirm
June Blender Advanced Help for Advanced Functions – #PSBlogWeek
Boe Prox A Look at Try/Catch in PowerShell

µ