Tired of hidden errors in your PowerShell scripts? Discover how validation and transformation attributes can bring precision and automation to your code!
When you assign information to variables, PowerShell, by default, doesnt do any extra work. It simply takes the value and stores it in the variable:
$test = '1.2.3.4'
$test
While convenient, this can mask errors in larger scripts and make debugging more difficult. If desired, you can ask PowerShell to invoke type conversions, run entire script blocks to validate and transform variable assignments, and emit errors when invalid data is encountered.
Assigning type
By assigning a type to a variable, any new value assigned to it must be of that type, or be convertible to it. If invalid values cannot be converted, an exception is raised:
[int]$id = 123
$id = 12.5
$id # now 12 (rounded)
$id = 'wrong' # raises an error, not convertible to int
Type assignments offer another great benefit: they allow you to select a more suitable type for your data, which can save you a lot of work. Take a look at this example:
$installed = '2.3.4.5'
$available = '10.1.2.3'
$shouldInstall = $installed -lt $available
$shouldInstall
Since PowerShell uses alphanumeric comparison for strings, "2.3.4.5" is considered greater than "10.1.2.3". You would need to invest significant effort to compare the numbers individually.
Alternatively, you can delegate this task to a more suitable type: once you assign the variables to the "[version]" type, they are compared correctly.
[version]$installed = '2.3.4.5'
[version]$available = '10.1.2.3'
$shouldInstall = $installed -lt $available
$shouldInstall
Refining types
Sometimes, there is no perfectly matching type for your data. In such cases, you can use a base type and refine it with a validation attribute.
For example, if you want a variable to store an ID number in the range of 1 to 1000 and be notified (via an exception) when an invalid ID number is encountered, you could use [int] as the base type and add [ValidateRange(1, 1000)]:
[ValidateRange(1,1000)][int]$id = 10
# this works
$id = 1000
# this raises exception
$id = -1
$id = 1001
There are several built-in validation attributes; however, [ValidateScript()] is the most flexible, as it allows you to define the validation criteria yourself.
Heres an example of a variable that can only store file paths to files that actually exist in your Windows folder:
[ValidateScript({ Test-Path -Path $_ -PathType Leaf})][string]$filepath = 'C:\Windows\explorer.exe'
# this works
$filepath = "$env:windir\notepad.exe"
# this fails
$filepath = "c:\test.txt"
Transforming data
By adding transformation attributes, you can instruct a variable on how to interpret an assignment and convert it into any form or shape you desire.
Heres an example of a custom transformation attribute:
class ServerIdAttribute : System.Management.Automation.ArgumentTransformationAttribute
{
[object]
Transform([System.Management.Automation.EngineIntrinsics]$engineIntrinsics, [object] $inputData)
{
################## Start of custom code ##################################
# variable assignment is found in $inputData
# user input was already a well-formed server name
if ($inputData -match '^Server\d{4}$')
{
return $inputData
}
# user input was a number, construct server name
elseif ($inputData -is [int])
{
return 'Server{0:d4}' -f $inputData
}
# user input was a string, can it be converted to a number?
elseif ($inputData -as [int])
{
return 'Server{0:d4}' -f ($inputData -as [int])
}
# invalid user input
else
{
throw "Server names must be 'ServerXXXX' where 'XXXX' is a number."
}
################## End of custom code ###################################
}
}
This template remains the same; only the code between the markings changes, determining how $inputData (the assignment) should be converted before being stored in the variable.
The class name defines the name of your transformation attribute and must always end with 'Attribute.' In this example, the class name is ServerIdAttribute, so the transformation attribute is named [ServerId()].
Once you have defined this attribute, you can assign variables like this:
[ServerId()]$serverName = 12
The variable now contains 'Server0012'. Since variables retain their types and attributes, whenever you assign a number to the variable, it will automatically generate a server name with a four-digit number:
PS C:\> $serverName
Server0999
PS C:\> $serverName = "98"
PS C:\> $serverName
Server0098
PS C:\> $serverName = 'Server0123'
PS C:\> $serverName
Server0123
Transformation attributes remain active throughout the lifetime of a variable. Whenever new values are assigned, the code inside the transformation attribute is executed.
Your ultimate PowerShell Cheat Sheet
Unleash the full potential of PowerShell with our handy poster. Whether you're a beginner or a seasoned pro, this cheat sheet is designed to be your go-to resource for the most important and commonly used cmdlets.The poster is available for download and in paper form.
Related links
- ScriptRunner ActionPacks will help you automate tasks
- Try out ScriptRunner here
- ScriptRunner: Book a demo with our product experts