Encountering errors is all part of the PowerShell routine. Well, unless you’re like me, in which case you’ve never encountered a scripting error. In fact, I had to force the scripts in this article to error out just so I could see what it feels like. Dialing back the heavy amounts of sarcasm for a moment, what should we do when we encounter scripting errors? We embark on the ancient battle that has gone on for generations called “error handling.”
What is error handling?
Error handling is the action of dealing with or “handling” errors encountered in a script. It involves dealing with both unexpected and expected errors. With proper foresight and testing, a script should account for the majority of errors it could encounter. Anticipating, detecting, and addressing conditions that could result in an error is the sign of a seasoned script writer.
Terminating versus nonterminating errors in PowerShell
While there are probably thousands of ways a PowerShell script can encounter an error, the key is to know the distinction between terminating and nonterminating errors. Terminating errors are known as exceptions. Exceptions terminate the current execution process or are caught by a Try, Catch, and Finally block (which we’ll cover a bit later). On the other hand, nonterminating errors write the encountered error to the pipeline, then continue executing the script.
ErrorAction in PowerShell
When PowerShell encounters an error, the default behavior is to continue executing the script if it can. This behavior is set with the global variable $ErrorActionPreference
. By default, this variable is set to Continue
, but you can set several other options. Error behavior can also be set per command using the -ErrorAction
common parameter, which is available to every PowerShell command. Here are the options available with the $ErrorActionPreference
and -ErrorAction
parameters:
Continue
: Writes the error to the pipeline and continues executing the command or script. This is the default behavior in PowerShell.Ignore
: Suppresses error messages and continues executing the command. The errors are never written to the error stream.Inquire
: Pauses the execution of the command and asks the user how to proceed. Cannot be set globally with the$ErrorActionPreference
variable.SilentlyContinue
: Suppresses error messages and continues executing the command. The errors are still written to the error stream, which you can query with the$Error
automatic variable.Stop
: Displays the error and stops executing the command. This option also generates anActionPreferenceStopException
object to the error stream.Suspend
: Suspends a workflow that can later be resumed. TheSuspend
option is only available to workflows.
I appreciate that the default behavior of PowerShell is set to continue executing the script or command after encountering a nonterminating error. This behavior sets it apart from many other programming languages. But there are times when you’ll want to take advantage of the -ErrorAction
options to deal with an error instead of pretending it doesn’t exist (which is what I do with most of my problems).
Anticipating errors in a PowerShell script
One of the most useful skills to develop when writing scripts is the ability to anticipate errors you could encounter. Anticipating errors and proactively addressing them produces highly dependable scripts.
For example, if you write a script that depends on an external file to function properly, it’s feasible to assume a situation where that file no longer exists, its path has changed, or the file name has changed.
Another example is a script that connects to a remote endpoint and returns information. What happens if that remote endpoint is offline and the connection fails?
The more you work with PowerShell, the easier it is to anticipate the types of errors you could encounter and handle them proactively. Now, if I could just anticipate my typing errers …
PowerShell error handling example
Error handling with PowerShell helps ensure that an unexpected exception does not let a script continue to cause issues. Let’s take a look at a script that makes sure only cool people have access to all the cool stuff. (See Get-Content.)
$GroupMembership = Get-ADGroupMember -Identity "Cool Users"
$UpdatedCoolList = Get-Content \\FileShare\Location\CoolPeople.csv
Foreach($Person in $GroupMembership){
If($UpdatedCoolList -notcontains $Person){
Remove-ADGRoupMember -Identity "Cool Users" -User $Person
}
}
Works great! However, Kris Powell found out about the cool list. Angry at being left off the list, he performs an action that proves we are right to keep him out. He deletes CoolPeople.csv.
Next time the script runs, we get an exception:
With a blank $UpdatedCoolList
variable, it removes everyone’s access — very not cool, Kris.
ErrorAction example in PowerShell
Before we deal with Kris’s sabotage, let’s look at an example of the -ErrorAction
parameter in use. We can use -ErrorAction Stop
to turn a nonterminating error into a terminating exception.
$UpdatedCoolList = Get-Content \\FileShare\Location\CoolPeople.csv -ErrorAction Stop
You can also set the default action of all errors to stop by setting the error action global variable.
$ErrorActionPreference = "Stop".
Setting the global variable is generally frowned upon. You have much more control if you set the parameter on each command rather than setting it globally. Just remember, in most cases, a cmdlet generates a nonterminating exception, but error handling with PowerShell requires a terminating exception to work.
Try/Catch/Finally
After the -ErrorAction
is set to Stop, we can wrap it in a Try/Catch
block. Try is where we run the command, and Catch is what runs if Try encounters a terminating exception. (See Out-File.)
Try {
$UpdatedCoolList = Get-Content \\FileShare\Location\CoolPeople.csv -ErrorAction Stop
}
Catch {
$_.Exception | Out-File C:\cool_log\krisdidit.log -Append
Break
}
In this script block, we captured the exception and put it in a log file. Break was used to exit the script so it would not continue. If we had left the Catch block empty, it would absorb the error and continue the script.
The last piece of the error handling with a Try/Catch/Finally
block is the Finally. This is used to provide actions that always run before exiting the script (or continuing on). It is mostly used for cleanup. You can use it to close database connections, remove files, or close file locks. Finally allows you to revert any changes done as a result of the script, whether an exception is encountered or not.
Conclusion
That’s it for the basics. Whenever you are dealing with elements that are volatile or out of your control, error handling is a great way to make sure that your script won’t cause problems if things go wrong.
If you’re looking for ways to test your newly developed skills, check out PDQ Deploy and Inventory. With Deploy and Inventory, you can run scripts remotely on all your endpoints in a flash — or just deploy scripts to the people on your cool list. The choice is yours.