Getty Images

Tip

Stop errors with these PowerShell ValidatePattern examples

Using regular expressions to validate input information and generate a tailored error message on a new user script can prevent unnecessary support tickets.

When you write scripts for other people, you need to expect the unexpected.

It's a useful skill to catch issues and deliver an appropriate error message when scripting in PowerShell. You may have nontechnical people who use your scripts who need to know what they did wrong if their input produces an error. One way to test a function is to validate user input. The PowerShell ValidatePattern attribute will generate an error message if user input does not match your regular expression. This method of validating input will save time and reduce problems, but it requires some additional coding to produce an error message that makes sense to the end user.

What is ValidatePattern?

Advanced functions in PowerShell let you specify several types of parameter validations.

The first is the parameter type itself. For example, if we want a string for a specific parameter, we use the [string] type and the function will only allow strings to be passed to that parameter.

The second form of parameter validation is through the various validation attributes that confirm several aspects of the passed value. One validation attribute is called ValidatePattern.

ValidatePattern compares the passed parameter to a regular expression and rejects the value if it doesn't match. ValidatePattern works on other parameter types besides strings by converting the value to a string and then running the regular expression comparison. You may get unexpected results on non-string parameters, so be sure to test thoroughly before using anything in production.

How to use ValidatePattern

Since ValidatePattern checks function parameters, it is placed in a param block, preceding the parameter you wish to validate with a pattern.

For example, if we were to use a phone number regular expression example, then we might have a parameter like the following:

param (
    [ValidatePattern('\(?(?<areaCode>\d{3})\)?(-| )?(?<first>\d{3})(-| )?(?<second>\d{4})')]
    [string]$PhoneNumber
)

The following function converts any passed phone number to the correct format:

Function Format-PhoneNumber {
    [OutputType([string])]
    param (
        [ValidatePattern('\(?(?<areaCode>\d{3})\)?(-| )?(?<first>\d{3})(-| )?(?<second>\d{4})')]
        [string]$PhoneNumber
    )
    $regex = '\(?(?<areaCode>\d{3})\)?(-| )?(?<first>\d{3})(-| )?(?<second>\d{4})'
    $phoneNumber -match $regex | Out-Null
    Write-Output "($($Matches.areaCode))-$($Matches.first)-$($Matches.second)"
}

You can test the function by attempting to pass an invalid phone number:

Format-PhoneNumber -PhoneNumber 123
ValidatePattern error message after detecting faulty input.
PowerShell will give an error message after the ValidatePattern attribute detects faulty input.

The error message is only useful if you can read regular expressions, which could be a problem depending on who gets the message. Be sure to write a clear help message with the HelpMessage attribute:

param (
    [Parameter(
        HelpMessage = 'A phone number with 10 digits is expected.'
    )]
    [ValidatePattern('\(?(?<areaCode>\d{3})\)?(-| )?(?<first>\d{3})(-| )?(?<second>\d{4})')]
    [string]$PhoneNumber
)

Now the user can try the Get-Help command for assistance:

Get-Help Format-PhoneNumber -Parameter PhoneNumber
Custom PowerShell help message.
By adding a custom help message, the user gets a less technical explanation of the desired input for the PowerShell script.

If we pass the right value, the function will format the phone number:

Format-PhoneNumber -PhoneNumber 1234567890
Screenshot of input validation and formatting.
After the entered number passes the validation check, the script formats the input.

I wrote this function for a PowerShell script that syncs users from a human resource information system into Active Directory and Microsoft Entra ID, formerly Azure Active Directory. In this case, the phone number field was a string that a user could enter in several formats, which the function would standardize for the IT system.

How to check email address input

In another coding scenario, imagine you have written a sophisticated function that performs several tasks across your infrastructure -- only to have it fail at the end because you entered an invalid email address for the notification message.

Email addresses can be hard to validate, depending on how accurate you want them. The emailregex.com site uses the following format to check email addresses:

Function Validate-EmailAddress {
    [cmdletbinding()]
    param (
        [ValidatePattern(@'
^(?(")(".+?(?<!\\)"@)|(([0-9a-z]((\.(?!\.))|[-!#\$%&'\*\+/=\?\^`\{\}\|~\w])*)(?<=[0-9a-z])@))(?(\[)(\[(\d{1,3}\.){3}\d{1,3}\])|(([0-9a-z][-\w]*[0-9a-z]*\.)+[a-z0-9][\-a-z0-9]{0,22}[a-z0-9]))$
'@
        )]
        [string]$EmailAddress
    )
}

While it works, it's a challenge to understand this regular expression. You might want a simpler regular expression that includes your organization's domain. You could use the System.Net.Mail.MailAddress class instead:

Function Validate-EmailAddress {
    [cmdletbinding()]
    param (
        [System.Net.Mail.MailAddress]$EmailAddress
    )
}

In that example, .NET does the validation. This is one example of using other tools to do the work rather than writing a complicated regular expression that you will not understand when you review it in the future.

How to validate file and folder input

You might already use a common parameter to accept paths to files or folders. Looking at the path to a file or a folder is a good opportunity for a regular expression. However, you'll be better served using the .NET types for both instead.

For files use System.IO.FileInfo and for directories use System.IO.DirectoryInfo:

Function Validate-Path {
    [cmdletbinding()]
    param (
        [System.IO.FileInfo]$File,
        [System.IO.DirectoryInfo]$Folder
    )
}

If you need to accept a file with a specific extension, this is where regular expressions help the validation process. In the following example, the script ingests a CSV file and one of the parameters is the path to the CSV file:

Function Ingest-CSV {
    [cmdletbinding()]
    param (
        [System.IO.FileInfo]$CsvPath
    )
}

We know the $CsvPath parameter needs to end with .csv so you can use the $ anchor in a regular expression, meaning the end of a string:

'\.csv$'

In this case the period in \. is an escape character in the regular expression.

When the System.IO.FileInfo object type is converted to string, we get the path of the file with the command:

([System.IO.FileInfo]'C:\blah.txt').ToString()

With that in mind, we can use the same Ingest-CSV function, but simply add the ValidatePattern:

Function Ingest-CSV {
    [cmdletbinding()]
    param (
        [ValidatePattern('\.csv$')]
        [System.IO.FileInfo]$CsvPath
    )
    $CsvPath
}

If we pass a non-CSV path, then PowerShell will generate an error message.

In another example, you can check that a user specifies a file within a file share. Start with the ^ anchor, which indicates the beginning of the match, and follow with the share path, escaped appropriately. For example, if the share was \\dc1\it-share\folder, then your function might look like the following:

Function Validate-SharePath {
    [cmdletbinding()]
    param (
        [ValidatePattern('^\\\\dc1\\it-share\\folder')]
        [System.IO.FileInfo]$SharePath
    )
    $SharePath
}

And we can test this by passing a valid path:

Validate-SharePath -SharePath \\dc1\it-share\folder\blah.txt

How you can benefit from ValidatePattern

PowerShell's ValidatePattern attribute unleashes a lot of potential with regular expressions, especially when you write functions in scripts that other people will use. Your scripts can reduce a lot of unexpected inputs and decrease the work spent rewriting code to accommodate every possible input scenario.

Parameter validation adds another layer of protection so a function executes as expected, but don't go too far overboard with overly complex regular expressions. You can cover parameter validation with the correct object type.

You can use ValidatePattern in conjunction with other types besides strings -- as shown in the file type and file share examples -- so don't be afraid to try something creative.

Anthony Howell is an IT expert who is well-versed in multiple infrastructure and automation technologies, including PowerShell, DevOps, cloud computing, and the Windows and Linux operating systems.

Dig Deeper on IT operations and infrastructure management