ToastIT
July 22, 2015

Giving ValidateScript a Makeover

Posted on July 22, 2015  •  3 minutes  • 577 words

I Feel Validated

One of the awesome things you get when adding the CmdletBinding attribute to you functions (you are including CmdletBinding, right?) is the ability to use Parameter Validation Atributes . These atributes include nifty options like allowing or disallowing null values, specifying a valid amount of items that an aray parameter can accept, specifying a set of pre-determined valid values.

All of these are awesome, and save you a fair amount of work in manually validating user input before using it within the rest of the function. They are checked before your code even runs and gives the end user nice feed back on what was wrong and what to do to fix it.

function Test
{
    [CmdletBinding()]
    Param
    (
        [ValidateSet('John','Jane')]
        [string] $Name
    )
}

This test fucntion has a name parameter, and the ValidateSet attribute will only allow John or Jane as valid values. If I try to pass in ‘Windos’, the function doesn’t run, and PowerShell lets me know why.

Very Flexible, Yet So Ugly

I would argue that the most flexible, and possibly useful, validation atribute is ValidateScript. It allows you to specify a script which is run against the user’s supplied value. (Note that the value being validated is assigned to the $_ variable.)

Given that you’re specifying your own script, you’re free to test literally anything.

With this flexibility, however, comes (subjectivly) ugly and less than helpful feedback for the user.

function Test-PC
{
    [CmdletBinding()]
    Param
    (
        [ValidateScript({ Test-Connection $_ -BufferSize 16 -Count 1 })]
        [string] $ComputerName
    )
}

This function takes a Computer Name, but will only run if Test-Connection runs against the supplied name successfully. If I pass in a name I know doesn’t exist the validation fails.

When I read the resulting message, which just blurts out the full validation script, I see: ‘Figure out what you did wrong and don’t do it again. Good luck!’

A Better Option

You’re in no way limited to using existing cmdlets in your ValidateScript atribute. You can even specify you’re own functions.

Whenever I use ValidateScript I always like to create my own Validate-* functions, in which I can do as complex a test as I want and also control the error message that is returned to the user. Your function should return $true if the parameter value is valid, and throw an error message if it is not valid.

function Validate-ComputerName
{
    Param
    (
        [string] $ComputerName
    )

    if (Test-Connection $ComputerName -BufferSize 16 -Count 1)
    {
        $true
    }
    else
    {
        throw "The computer $ComputerName is either offline or does not " +
            'exist. Please check that the value is correct and try again.'
    }
}

function Test-PC
{
    [CmdletBinding()]
    Param
    (
        [ValidateScript({ Validate-ComputerName $_ })]
        [string] $ComputerName
    )
}

As you can see, the text we specified after the throw statement is presented to the user and is a lot easier to understand what the issue is and what needs to be done to correct the situation.

Closing Notes/ TL;DR

comments powered by Disqus
Follow me