© Adam Bertram 2020
A. BertramBuilding Better PowerShell Codehttps://doi.org/10.1007/978-1-4842-6388-4_11

11. Handle Errors Gracefully

Adam Bertram1  
(1)
Evansville, IN, USA
 

Believe it or not, your PowerShell script isn’t going to work right all the time. It will fail and fail hard sometimes. A novice developer typically doesn’t worry much about error handling. Error handling is one of those topics that separates the novices from the professionals.

To create a production-ready, robust PowerShell solution, you must ensure all the ways your scripts can fail are properly handled. Inevitably, they will throw an error when you least expect it and it’s important to catch that error to either fix the problem mid-run or exit gracefully.

Force Hard-Terminating Errors

Unlike many other languages, PowerShell has two types of exceptions/errors terminating and non-terminating errors. When encountered, a non-terminating error does not stop script execution. It does not terminate code execution. A hard-terminating error, on the other hand, does.

You should not rely on non-terminating errors under most circumstances. Why? Because you have much more control with hard-terminating errors. Non-terminating errors are essentially just red text on the screen. Just like using the Write-Host cmdlet , you don’t have much, if any, control over those errors. Always use hard-terminating errors.

There are a couple of different ways to force a hard-terminating error, either through using the common ErrorAction parameter or using the global $ErrorActionPreference automatic variable.

Let’s say you have a script that copies an ACL from one file to another but you accidentally pass a nonexistent file to it.
$someFile = Get-Item -Path 'a file that does not exist'
$acl = Get-Acl -Path $someFile.FullName
Set-Acl -Path 'a file that exists' -AclObject $acl
If you’d run the preceding script, you’d see three different errors as you can see in Figure 11-1. This happens because Get-Item couldn’t find the file. It told you so but didn’t stop the script. It returned a non-terminating error. Since it didn’t terminate the script, the code just kept going unnecessarily. Lines 23 depend on line 1, so if line 1 fails, there’s no reason to run lines 23.
../images/501963_1_En_11_Chapter/501963_1_En_11_Fig1_HTML.jpg
Figure 11-1

Three non-terminating errors

Instead of relying on a non-terminating error, instead “convert” that error to hard-terminating error. One way to do that on a per-command basis is to use the ErrorAction parameter and set it to Stop as shown here:
$someFile = Get-Item -Path 'a file that does not exist' -ErrorAction Stop
$acl = Get-Acl -Path $someFile.FullName
Set-Acl -Path 'a file that exists' -AclObject $acl
Now when you run the script, you only receive one error. PowerShell terminated the script before it had the change to execute lines 23. Not only that but you can now run code based on if the error/exception is thrown by putting this in a try/catch block.
try {
    $someFile = Get-Item -Path 'a file that does not exist' -ErrorAction Stop
    $acl = Get-Acl -Path $someFile.FullName
    Set-Acl -Path 'a file that exists' -AclObject $acl
} catch {
    Write-Warning -Message $_.Exception.Message
}

When you run the script now, you receive a nice warning. PowerShell threw the exception that Get-Item created and the catch block caught it and ran the code inside.

You could tack on the -ErrorAction Stop parameter to each of your commands or, if you want to globally turn non-terminating errors into hard-terminating errors, you can use $ErrorActionPreference. By setting $ErrorActionPreference to Stop as the first line of the script, you’re forcing all commands below that script to only return hard-terminating errors.
$ErrorActionPreference = 'Stop'
try {
    $someFile = Get-Item -Path 'a file that does not exist'
    $acl = Get-Acl -Path $someFile.FullName
    Set-Acl -Path 'a file that exists' -AclObject $acl
} catch {
    Write-Warning -Message $_.Exception.Message
}

Use $ErrorActionPreference and the ErrorAction parameter as much as possible to ensure you can control those exceptions!

Further Learning

Avoid Using $?

It’s important to write code as simple as possible. Convoluted, complex code does no good to anyone regardless if you think you look smart doing it or if you think it’s saving you a few characters of typing. You should always focus on writing code that others can read and understand.

PowerShell has a default variable called $? which some are tempted to use. Just looking at this variable, what do you think it does? You undoubtedly have no idea. There’s no name or indication of what this variable’s purpose is. The only way to find out is to read some documentation. This is not the type of code you want to write.

To save you some time scouring through the documentation, this variable indicates if the last command executed was successful or not by returning True or False. Not only is this variable’s name unintuitive, but it also just returns a single Boolean value if an error occurred. It provides no other information.

Using part of the preceding script as a demo, let’s say you instead went the lazy way and decided to silence all soft-terminating errors completely by setting the ErrorAction parameter to Ignore. You’re then using the $? variable to make a decision whether or not to run the other code.
$someFile = Get-Item -Path 'a file that does not exist' -ErrorAction Ignore
if (-not $?) {
    Write-Warning -Message $_.Exception.Message
} else {
    $acl = Get-Acl -Path $someFile.FullName
    Set-Acl -Path 'a file that exists' -AclObject $acl
}

At the end of the day, the preceding code is accomplishing the same goal as the earlier try/catch example with a hard-terminating error. But, by using the much less known $? variable, the script isn’t near as readable.

Further Learning

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
13.59.9.236