PowerShell Error Handling Explained
| technology | system admin - Ross Heintzkill

PowerShell Error Handling Explained

Murphy's Law is an old saying that promises, "Anything that can go wrong will go wrong." Coders and programmers know this saying particularly well. If you've spent any time writing code, you understand why. What coder doesn't know the feeling of writing the perfect script that accomplishes just what you need, but some external variable pops up that causes it to malfunction?

In this blog post, we're going to discuss the process for anticipating potential errors, mistakes and problems with your scripts and code: error handling. We'll look at a small, simple program and insert some key lines and functions that will show you how to spot errors and identify them, rather than let your programs and machines churn along with faulty inputs.

What is a PowerShell Exception?

Quick Definition: A PowerShell exception is an error that happens while running PowerShell scripts, errors that PowerShell needs to be handled for it. PowerShell will try to handle errors on its own, but exceptions are, as the name may suggest, exceptions to its ability to do so. When an exception occurs, the phrase used is "throw". Handling a "thrown" exception means "catching" it, which is telling the script what to do. If a thrown exception isn't caught, the script stops.

What is PowerShell Error Handling?

Quick Definition: Error handling in PowerShell is a process of analyzing your code for where exceptions could occur, for what exceptions could happen, and then writing code that anticipates and handles those exceptions.

An Overview of PowerShell Error Handling [VIDEO]

In this video, Don Jones covers error handling in PowerShell. Something that sets a good script apart from a truly great one is its ability to respond to errors that you anticipated ahead of time. Watch as Don demonstrates the fine art of error handling.

When is Error Handling Necessary?

One of the things that really sets apart a good script from a great script is the script's ability to respond to errors that you can anticipate ahead of time. It's called Error Handling.

If you're writing code or programming behavior for a computer, doing error handling can help in a number of ways. First of all, written well, a program can inform you what's wrong — like a patient explaining symptoms to their doctor. Not only that, a program that stops at an exception won't waste time and resources continuing a process that's doomed to failure.

Don't mistake error handling with faulty scripting. Errors in code can be a problem too, but usually it's easy to spot those before running the program. Most people know the old "missing semicolon ruins day" problem. Error handling is about finding externalities — variables that you and your program don't have control over while the program is doing its job.

Since the goal of error handling is to anticipate errors and then deal with them as they occur rather than just allow PowerShell to explode, the first step is spending some time with the program. Take a look at the following script and try to spot where you think errors might occur.

param (
        $computername = 'localhost'
)
   $os = Get-WmiObject -Class Win32_OperatingSystem -ComputerName $computername
   $system = Get-WmiObject -Class Win32_ComputerSystem -ComputerName $computername
   $props = @ {        'ComputerName'=$computername;
                'OSVersion'=$os.caption;
                'TotalRAM'=$system.TotalPhysicalMemory;
                'Manufacturer'=$system.Manufacturer}
   $obj = New-Object =TypeName PowerShellObject -Property $props
   Write-Output $obj

Now, because this is a short one, it's reasonably safe to assume that errors are going to happen on one of these two lines – if they happen anywhere:

   $os = Get-WmiObject -Class Win32_OperatingSystem -ComputerName $computername
   $system = Get-WmiObject -Class Win32_ComputerSystem -ComputerName $computername

These are really the only two lines that are doing anything. They're "leaving" the code and working with data from an external source. They're working with an external entity. Some of the errors that we can anticipate include the computer in question not being available when we try to query it, or it could be that we don't have permission to query it. There are a few things that could go wrong, but they'll likely happen on those lines.

How to Code PowerShell Error Handling

There are three big steps to writing error handling in PowerShell: first is to identify where an error may occur: which command. Then, you put that command inside a try { } block. Third, you put inside a catch { } block what should be done if the error happens. We'll walk through these three steps next.

First of all, choose one command — typically one. While it's possible to do error handling for a block of commands, it's usually best to limit your focus. Plus, the method we'll be demonstrating below works best on one command. So, choose your one command and wrap it in a try-catch block.

try {
   $os = Get-WmiObject -Class Win32_OperatingSystem -ComputerName $computername
} catch {
}

That's part of the first step to writing PowerShell error handling: identify the command you think might cause an error. The second step is to put it inside a try { } block. Although the third step is to put inside the catch { } block whatever we want to do in the event of an error occurring, there's another tweak we have to do first.

Most PowerShell commands are going to need a little bit of extra work. Still inside the try { } block, you'll have to add a parameter to your function called ErrorAction and set it to "Stop".

try {
   $os = Get-WmiObject -Class Win32_OperatingSystem -ComputerName $computername -ErrorAction Stop
} catch {
}

Sometimes you'll see that as -EA, which is what we'll abbreviate it as going forward. Now, it's possible you might want to take a different action based on the type of error that occurs. And so, you can capture the error in an -ErrorVariable. We're going to call it "x".

try {
   $os = Get-WmiObject -Class Win32_OperatingSystem -ComputerName $computername -EA Stop -ErrorVariable x
} catch {
}

Sometimes you'll see that abbreviated -EV, which is again what we'll abbreviate it as going forward. Also, it's important to note that the variable name for the error does not include a dollar sign ($).

Next, we have to ask ourselves what we want to do about it when an error occurs? Well, let's say we want to write the computer name that failed to a log file. To do that, we take the computer name variable $computername, and pump it to a file. And Append it to whatever else is in there:

try {
   $os = Get-WmiObject -Class Win32_OperatingSystem -ComputerName $computername -EA Stop -EV x
} catch {
   $computername | out-File c:\errors.txt -Append
}

Maybe we also want to display a warning on the screen. We can make up our own error message, but for now let's keep it pretty boilerplate: "Error talking to $computername : $x"

try {
   $os = Get-WmiObject -Class Win32_OperatingSystem -ComputerName $computername -EA Stop -EV x
} catch {
   $computername | out-File c:\errors.txt -Append
   Write-Warning "Error talking to $computername : $x"
}

Note, be sure to keep that additional space behind the first variable "$computername". And, by using double quotation marks, we can include these variables and they'll be expanded into their values.

How to Optimize Your PowerShell Error Handling

The trick to good PowerShell error handling is applying logic and reasoning to your code. Really good error handling anticipates errors and thinks about their consequences. Before we move on, let's take a look at what our changes so far have gotten us:

try {
   $os = Get-WmiObject -Class Win32_OperatingSystem -ComputerName $computername -EA Stop -EV x
} catch {
   $computername | out-File c:\errors.txt -Append
   Write-Warning "Error talking to $computername : $x"
}
$system = Get-WmiObject -Class Win32_ComputerSystem -ComputerName $computername
   $props = @ {        'ComputerName'=$computername;
                'OSVersion'=$os.caption;
                'TotalRAM'=$system.TotalPhysicalMemory;
                'Manufacturer'=$system.Manufacturer}
   $obj = New-Object =TypeName PowerShellObject -Property $props
   Write-Output $obj

But let's think about this. If the computer doesn't respond to the first query,

try {
   $everything_is_ok = $true
   $os = Get-WmiObject -Class Win32_OperatingSystem -ComputerName $computername -EA Stop -EV x
} catch {
   $computername | out-File c:\errors.txt -Append
   Write-Warning "Error talking to $computername : $x"
}

Then, down inside the catch { }, we'll tell the program to set that variable to false if and when we get an error:

try {
   $everything_is_ok = $true
   $os = Get-WmiObject -Class Win32_OperatingSystem -ComputerName $computername -EA Stop -EV x
} catch {
   $computername | out-File c:\errors.txt -Append
   Write-Warning "Error talking to $computername : $x"
   $everything_is_ok = $false
}

And then, wrap the output in an if statement regarding that variable:

if ($everything_is_ok) {
$system = Get-WmiObject -Class Win32_ComputerSystem -ComputerName $computername
   $props = @ {        'ComputerName'=$computername;
                'OSVersion'=$os.caption;
                'TotalRAM'=$system.TotalPhysicalMemory;
                'Manufacturer'=$system.Manufacturer}
   $obj = New-Object =TypeName PowerShellObject -Property $props
   Write-Output $obj
}

So what is this doing? What it's telling our program is that if our first function fails, the lines inside our catch { } will execute. When that happens, our variable becomes false, and none of the subsequent lines wrapped inside the if { } will ever execute. After all, if the first query failed, there's no point in trying the second one – it'll almost certainly fail too.

Once again, this is a small example. But hopefully it helps illustrate one of the approaches you can take to uncovering not just the first thrown exception in a program, but what to do about subsequent functions too.

How to Test Your PowerShell Error Handling

It's always a good idea to check your code, both for success states and failure states, and this program is no different. If you can do it from your own device, go ahead and save the above code and give it a test. The program we've written is going to search for a server "localhost", which should obviously be discoverable on the network. If you're like us, running the program results in success.

Of course, it's not enough that your successes succeed correctly. You also want your failures to fail correctly. The way we can test for the errors we've anticipated is by changing our first parameter, "localhost" to the name of a server that definitely doesn't exist on the network. We went with "NOTONLINE".

After we run that, eventually there will be an error. We see: "Error talking to NOTONLINE" followed by the exception that occurred: "The RPC server is unavailable."

Not only that, but we can pull up errors.txt and see that the name of that computer was successfully logged to the file.

The thing to remember about Error Handling in PowerShell is the -ErrorAction. That tells the command, "Look, if a problem happens, rather than just keep going and trying to continue, I want you to stop and throw a complete exception — a trappable exception." Without the -EA Stop, no part of the try-catch block would function.

In fact, removing it shows quite a different result. We'll leave our "NOTONLINE" server name, remove the -EA Stop from our code, and try running the code again.

If you're doing this too, you might find that your Windows Management Instrumentation takes a while to time out. But once it finally does, you should see an error that's far less manageable. That's because this time the error comes directly from the command. It's not a warning generated by the code which detected an error, it's a full-on uncaught exception. Not only that, but the second command tried to run, and a different WMI error came through for that. Because the input for the second function was an error, the output of it is gibberish.

The difference should be obvious: if you can avoid a full-blown error and instead catch every exception as a warning and halt the functions, you do a lot for your program. Not only do you prevent time, energy, and resources from being wasted, but you also increase the chances of pinpointing where the error occurred and how you can fix it.

If you leave PowerShell to its own devices, it'll try to carry out your commands as best it can, with increasingly faulty data coming from error after error. Instead, tell it you want it to stop when there's an error so that you can fix what caused it. You should see now that the -ErrorAction Stop is the key to making ErrorHandling work in PowerShell.

Wrapping Up

If you're looking to master PowerShell, CBT Nuggets has training that will do much more than one blog post could. Here, we only had time to address some of the top-level ideas of PowerShell error handling, but consider this PowerShell 7 training that features 9 hours of training and 104 videos about scripts, and automation.

Download

Download

Ultimate Systems Administration Cert Guide

A 158-page guide to every Microsoft, VMware, Citrix, AWS, Google, and Linux certification, and how they fit into your career.

I have read and understood the privacy policy, and am able to consent to it.

LEARNING ON MOBILE

Learn anytime anywhere with our mobile apps.

I have read and understood the privacy policy and am able to consent to it.

© 2021 CBT Nuggets. All rights reserved. Terms | Privacy Policy | Accessibility | Sitemap | 1550 Valley River Drive, Eugene, OR 97401 | 541-284-5522
CBT Nuggets