Simplify Your PowerShell Script with Parameter Validation


Summary: Learn how to simplify your Windows PowerShell Script by using parameter validation attribute.

Weekend Scripter

Microsoft Scripting Guy, Ed Wilson, is here. I thought I would ask Glenn Sizemore to write today’s blog about parameter validation attributes.

Photo of Glenn Sizemore

Glenn Sizemore is a technical marketing engineer in the Microsoft business unit at NetApp, where he combines Microsoft technologies with NetApp hardware by using Windows PowerShell. Glenn started scripting early in his IT career, and he has made a living off it ever since. Along the way, he started a blog, wrote a book, and is the proud father of two beautiful children.

The 2011 Scripting Games have come and gone, and almost 2000 scripts were written in this year’s contest. This resulted in a hundreds of thousands of lines of Windows PowerShell code, some better than others, but all executed to the authors best ability. Fortunately with Windows PowerShell, almost all mishaps were a result of the author simply not knowing about or how to use a feature within the Windows PowerShell language. Today, we will try to shed light on a feature that was introduced in Windows PowerShell 2.0: parameter validation.

Simply put, parameter validation is a means for Windows PowerShell to validate a parameter’s value before the body of the script or function is run. Often parameter validation can significantly clean up one’s code, while increasing performance. So let’s start by examining how you would validate a parameter in Windows PowerShell 1.0.

In this example, you have three parameters Name, Age, and Path. For the script to function correctly, you need the Name to be Tom, Dick, or Jane. The Age parameter must be between 21 and 65, and the Path parameter must point to a valid folder. In Windows PowerShell 1.0, your code would look something like this:

Function Foo
{
    Param(
        [String]
        $Name
    ,
        [Int]
        $Age
    ,
        [string]
        $Path
    )
    Process
    {
        If (“Tom”,”Dick”,”Jane” -NotContains $Name)
        {
            Throw “$($Name) is not a valid name! Please use Tom, Dick, Jane”
        }
        If ($age -lt 21 -OR $age -gt 65)
        {
            Throw “$($age) is not a between 21-65”
        }
        IF (-NOT (Test-Path $Path -PathType ‘Container’))
        {
            Throw “$($Path) is not a valid folder”
        }
        # All parameters are valid so New-stuff”
        write-host “New-Foo”
    }
}

When executed, the end result appears to work perfectly, as shown here.

Image of command output

There are several drawbacks to this approach. First of all, you had to dedicate a chunk of the script to validating the value of each parameter. The validation is only as good as the script written to check it. There is also the issue of the error messages. These too, are only as good as written—and we all know how much we like to write good verbose messages. Even if you do write a good descriptive error message, it does not point to the problem in the code. The error points to the throw statement in the code, so it is not very intuitive. Finally, if you ever want to make any modifications to the accepted parameter values, you would have to change those values in multiple places within the script.

For all these reasons and more, the Windows PowerShell team introduced parameter validation. Let us take a look at the previous example with parameter validation.

Function Foo
{
    Param(
        [ValidateSet(“Tom”,”Dick”,”Jane”)]
        [String]
        $Name
    ,
        [ValidateRange(21,65)]
        [Int]
        $Age
    ,
        [ValidateScript({Test-Path $_ -PathType ‘Container’})]
        [string]
        $Path
    )
    Process
    {
        write-host “New-Foo”
    }
}

First of all, you replace over 10 lines of code with three markup tags! More importantly, look at the output that is received when using the built-in validation tools:

Image of command output

Clean, verbose, and meaningful error messages are generated for free. You do not have to write anything! More importantly, if you wanted to adjust the accepted values, you only have to change them in one place. The toolbox available to you for parameter validation is vast and flexible. Currently, there are eleven dedicated parameter validation attributes that cover just about everything.

However, like everything in life, there are flaws in the system. For instance, if you assign a default value to a parameter, that value will not be validated because only passed parameters are validated. This is shown in the following example:

Function Foo
{
    Param(
        [Parameter(Mandatory=$false,ValueFromPipeline=$true)]
        [ValidateRange(21,65)]
        [Int]
        $Age = 95
    )
    Process
    {
        $age
    }
}

Intuition would imply that this code would throw an error if we attempt to use the default value—but remember, only user-supplied values are validated.

Image of command output

Although inconsistent, this is understandable because the validation attributes are only run during parameter binding, and the default value is processed after parameter binding has occurred. Of course, this really is not that big of a deal. You just have to know about the limitation, and script around it.

For example, if you want to test for the presence of a local registry key, you have two choices. You can validate the path again in the body of the script, or you can plan to encounter an error, as shown in the following example.

Function Foo
{
    Param(
        [Parameter(Mandatory=$false,ValueFromPipeline=$true)]
        [ValidateScript({Test-Path $_})]
        [String]
        $Key = ‘HKLM:\Software\DoesNotExist’
    )
    Process
    {
        Try
        {
            Get-ItemProperty -Path $Key -EA ‘Stop’
        }
        Catch
        {
            write-warning “Error accessing $Key: $($_.Exception.Message)”  
        }
    }
}

Of course, you should always assume that you will encounter an error, and write code that can survive the error. But the lesson here is if you implement a default value, remember that it is not being validated.

Another scenario where you would implement an additional check is if you needed to combine several parameters. For instance, if you had a script that took two parameters, Directory and FileName, and you combine them later in the script, you can’t test for their combined value by using the current validation attributes.

At first glance, one would think that the ValidateScript tag could simply access the values in sister parameters to perform more advanced validation. Alas, this is not possible. Under the covers, ValidateScript is a Where-Object script block that isn’t scoped to access any other parameters. These limitations aside, I highly encourage you to read about_Functions_Advanced_Parameters and start using these incredibly powerful attributes in your day-to-day scripting.

Remember that the Scripting Games are all about honing your scripting technique to make you better at your real job. If you don’t take what you learned this year and start using it, you will have to learn it again next year. If you do apply all the tips and tricks from this year, and the year after, and the year after that… well, that’s how you become a script ninja!

Happy scripting!

Thank you, Glenn. I love your comparison example of the v1 methods and the v2 methodology. Parameter validation attributes rock—and so do you! Great blog.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy

Comments (24)

  1. Great post – your notes came in handy today ๐Ÿ™‚

  2. Will Steele says:

    Aside from altering the $ErrorActionPreference is there a way to handle errors that results from failed validation?  For instance, if I have a non-terminating error, how would I be able to continue processing and just note the issue to an ErrorVariable?

  3. jrv says:

    @Anon

    Param (

      [Parameter()]$i = 1,

      [Parameter()]$j = 2

    )

    Set-StrictMode -Version 3.0

    Write-Host "i = $i; j = $j"

    You cannot place any code before a 'Param' statement except "#requires -version n.n" or other comments

  4. jrv says:

    Don't forget ErrorVariable!

    help common

    # list non-existent folder causing an error

    Get-Content xxxx -ea SilentlyContinue -ev +MyError

    $MyError

    Look Ma! No red lines anymore.  My errors have turned white.

    Good Luck and have fun.

  5. jrv says:

    function Test-Msg{

        Param(

             [Parameter(

                  Mandatory=$true,

                  HelpMessage='Count of things in the world'

         )][string]$count

         )

       # body here

    }

    Test-Msg # and follow instructions on the screen

  6. jrv says:

    #requires -version 2.0

    function t{

      param(

         [ValidateRange(21,65)]$x

      )

    $x

    }

    t 3 # fails

    t 21 # succeeds

  7. jrv says:

    @Anon – doesn't matter once you break a syntax rule.

    Advanced functions and scripts have strange behaviors once you place any executable line above a Param block.

  8. bshwjt says:

    Awesome for param.

  9. Anonymous says:

    This is great.  Can you additional add in text to decribe what the parameters are for?

    ie.

    0 – extracts data for people data

    1 – extracts data for vendor data

  10. JV says:

    Excellent! Best one page desciption of validation I have seen.  It reads easy and is instantly useful.  Link to help also a good idea.

  11. Klaus Schulte says:

    Hello Glenn,

    a great articel about one of my next learning points!!!

    I've got to tell you, that we would be best friend, because of

    1) the scripting games 2010, of course ๐Ÿ™‚

    2) this sentence: "Of course, you should always assume that you will encounter an error, and write code that can survive the error"

    3) and that sentence "If you donโ€™t take what you learned this year and start using it, you will have to learn it again next year"

    So I donร„'t have to say anything about the scripting games, error handling and what I'll try to do this time!

    kind regards, Klaus

  12. srikanth says:

    Thanks a lot, Glenn. An important topic described wonderfully simply ๐Ÿ™‚

  13. Ed Wilson says:

    @Will Steele You can also set -erroractionpreference (parameter alias -EA) on each PowerShell cmdlet. You also have the Try / Catch / Finally construction that can be used for structured error handling. Look at the PowerShell / Error Handling tags on the Hey Scripting Guy blog for some articles around these topics.

  14. Ed Wilson says:

    @JRV Error variable is a good one. Great suggestion.

  15. Panayot says:

    Is that example for PS2? As am geting error if I use [ValidateRange(21,65)]

  16. Anon says:

    Why does the following produce a warning?

    Set-StrictMode -Version 3.0

    Param (

       [Parameter()]$i = 1,

       [Parameter()]$j = 2

    )

    Write-Host "i = $i; j = $j"

    +     [Parameter()][int]$i = 1,

    +                            ~

    Assignment expression is not valid. The left hand side of an assignment operator needs to be something that can be assigned to like a variable or a property.

    Is multiple assignment in Param expressions illegal?

  17. Anon says:

    @jrv

    If that's the case, why isn't there an error when you remove the [Parameter()]$i = 1 line even though Set-StrictMode is still above the Param block?

  18. akamac says:

    Hello, Glenn. Thanks for the article.

    Is there any way to access other parameters values in ValidateScript scriptblock? Particulary I need to get value of dynamic parameter using $PSBoundParameters automatic variable (cause dynamic parameter is added through function), but hasn't succeeded. Is it exposed or not?

  19. software validation says:

    Verification and validation techniques applied throughout the development process enable you to find errors before they can derail your project.
    they are involved in lot of process improvements which will actually help the clients to deliver best software.

    http://automationandvalidation.com">software validation

  20. software validation says:

    Verification and validation techniques applied throughout the development process enable you to find errors before they can derail your project.
    they are involved in lot of process improvements which will actually help the clients to deliver best software.

    http://justpaste.it/softwarevalidation">software validation

  21. software validation says:

    This applies not only during the IQ, OQ and PQ validation phases, but far earlier,
    beginning with the first meeting, and continuing through analysis of your process.
    Software validation is a part of the design validation for a finished device,
    but is not separately defined in the Quality System regulation.

    http://softwarevalidation2.blog.com/2014/12/17/software-validation/">software validation

  22. Casey4 says:

    Hello Glenn and Ed,

    I’m having an issue with a mandatory integer in my function when using the following:

    [Parameter(Mandatory=$true)][int]$SessionID

    The issue is that even though it will always ask for the SessionID, if not provided it will not error out if no value is given as it defaults to zero. I understand that integers will default to zero in this situation, but I can’t seem to get the script to throw
    an error.

    I need the user to provide a number and this ID can be in the range 0-100. The number can be zero, but the function should not default or assume this value. I’ve tried setting Validate Range and defaulting the variable to -1. I’ve also tried different Validate
    Scripts and ValidateNotNullOrEmpty but still can not get it to error. Even neglecting to define the parameter as an integer and validating instead in the script doesn’t work.

    Please let me know if there is something I’m missing with this.

    Thanks,
    Casey

  23. software validation says:

    I really like examining and also following ones write-up when i locate them incredibly beneficial and also fascinating.
    That write-up is usually just as beneficial along with fascinating.Verification and Validation both are independent type of testing. Obviously,
    If we look both of these activities as a whole, we can also call it testing.

    http://softwarevalidation4.simplesite.com/">software validation

Skip to main content