Summary: Microsoft PFE, Ashley McGlone, talks about using Try, Catch, Finally blocks for error handling in Windows PowerShell.

Microsoft Scripting Guy, Ed Wilson, is here. Today’s guest blogger is Ashley McGlone, a Microsoft premier field engineer. Ashley is a popular speaker at our Windows PowerShell Saturday events. He regularly blogs about Active Directory and Windows PowerShell at Goatee PFE. You can follow him on Twitter as @GoateePFE. Take it away, Ashley...

Why do scripts have errors? That is a long philosophical debate, and there is more than one correct answer. The generally accepted answer is that they are written by humans, and humans are not perfect. Even when considering all of the reasons there are errors in scripts, the root cause is usually a difference in expectations. I expect the code or data to be X, but really it is Y. And that is why we need error handling.

There are many ways to handle errors in Windows PowerShell, including:

  • $Error.Clear(); Do-Something; If ($Error) {..} Else {..}
  • Trap
  • $ErrorActionPreference

Try, Catch, Finally is similar to a Trap block. Trap blocks generally catch any errors in the scope of the entire script or function. The beauty of Try, Catch, Finally is that it is like a localized Trap for a specific block of commands. This gives you great flexibility in your error handling. It generally works like this:

Try {

 # Do something tricky

}

Catch {

 # Run this if a terminating error occurred in the Try block

 # The variable $_ represents the error that occurred

 $_

}

Finally {

 # Always run this at the end

}

Tip  Introduced in Windows PowerShell 3.0, you can use CTRL-J in the ISE to insert a Try, Catch, Finally snippet template to save you some typing.

We can also catch multiple errors. Here is an example from the ISE snippet:

try

{

 1/0

}

catch [DivideByZeroException]

{

 Write-Host "Divide by zero exception"

}

catch [System.Net.WebException],[System.Exception]

{

 Write-Host "Other exception"

}

finally

{

 Write-Host "cleaning up ..."

}

Finding .NET exceptions to catch

Following the Catch keyword, you can add .NET exception types as shown in the previous script example. (Fancy programmers call errors “exceptions.”) These are optional. So where can I find that fanciness to put after the Catch? You can sometimes find these on MSDN. For me, the fastest way is using this little trick:

$Error[0] | fl * -Force 

Look at the following example output when we try to divide by zero. Then notice the $Error output from the second command. Usually, the .NET exception follows the ‘-->’ in the output:

PS C:\> 1/0

Attempted to divide by zero.

At line:1 char:1

+ 1/0

+ ~~~

 + CategoryInfo   : NotSpecified: (:) [], RuntimeException

 + FullyQualifiedErrorId : RuntimeException

 

PS C:\> $Error[0] | fl * -Force

PSMessageDetails  :

Exception    : System.Management.Automation.RuntimeException: Attempted to divide by zero. -->

      System.DivideByZeroException: Attempted to divide by zero.

       --- End of inner exception stack trace ---

       at System.Management.Automation.IntOps.Divide(Int32 lhs, Int32 rhs)

       at System.Dynamic.UpdateDelegates.UpdateAndExecute2[T0,T1,TRet](CallSite site, T0 arg0, T1 arg1)

       at System.Management.Automation.Interpreter.DynamicInstruction`3.Run(InterpretedFrame frame)

       at   System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)

TargetObject   :

CategoryInfo   : NotSpecified: (:) [], RuntimeException

FullyQualifiedErrorId : RuntimeException

ErrorDetails   :

InvocationInfo  : System.Management.Automation.InvocationInfo

ScriptStackTrace  : at <ScriptBlock>, <No file>: line 1

PipelineIterationInfo : {}

That’s how we find what to catch (or trap) when handling errors. You have to produce the error once, get the exception string from the extended $Error details, and then put that into square brackets following Catch. In this case either of the following would work:

catch [DivideByZeroException]

catch [System.DivideByZeroException]

This works for most error handling situations.

Tap...tap...tap...Is this thing on?

Sometimes Try, Catch, Finally will not catch your error. That’s because there are two kinds of errors in Windows PowerShell: terminating and non-terminating. For example, when I type:

PS C:\> dir HKLM:

I get errors in the middle of the output, but it keeps going. That is called a non-terminating error. However, if I try to divide by zero as in the previous example, that is a terminating error that stops the entire script.

You can force errors to terminate and hit your Catch block by using either of these methods:

  • $ErrorActionPreference = ‘Stop’
  • Use the common parameter: -ErrorAction Stop

In the interest of time, I will refer you to this previous blog post for a good explanation about how this works: 2014 Winter PowerShell Scripting Games Wrap Up #2.

How I’ve used Try, Catch, Finally

Most of my scripting revolves around the Active Directory module. Recently I was trying to use Get-ADObject with the ErrorAction parameter. Unfortunately, the cmdlet did not seem to obey the ErrorAction common parameter very well.

# No joy

$a = Get-ADObject -Identity $Id -Properties $Prop -ErrorAction SilentlyContinue

I need this functionality, because sometimes the object I was querying may not exist. This syntax would break the script if an error occurred, ignoring the ErrorAction parameter. To work around this, I enclosed the offending line of script in a Try block, and then I handled the error in the Catch block. Here is what I used:

try {

 $a = Get-ADObject -Identity $Id -Properties $Prop

}

catch {

 $a = $null

}

Notice that I left off the optional Finally block. This code met my needs perfectly.

I hope you’ve enjoyed learning about Try, Catch, Finally today. Don’t forget to read the Help for more information:

PS C:\> Get-Help about_Try_Catch_Finally

See these Windows PowerShell Help topics for related information:

  • about_Preference_Variables
  • about_CommonParameters
  • about_Throw
  • about_Trap

~Ashley

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy