PowerShell Pipeline

Exploring Errors in PowerShell

The first step in solving any problem is first learning why the problem occurred in the first place.

Errors happen regardless of what scripting or programming language that you use. Whether the error happens due to an issue in how the code was written or if the code is not being used in the way that it was intended. We know that errors can come in the non-terminating and terminating type as well as how to properly handle the errors when they occur. With PowerShell, not only can we handle the errors but we can also explore the error itself as it is an object like everything else in PowerShell! Using the error, we can uncover some useful things not only about the error itself, but also about what was being run at the time of the error.

First off, it is important to know that the error variable is called $Error and it is considered an automatic variable which means that you should never think about saving data to it. Doing so could have unintended issues and create more issues. $Error is also a collection which means that you can locate a specific error by an index such as the most recent error will be at the 0 index and the last error will be the final item in the collection, easily found by using -1 in the index.

throw 'this will  fail'
Get-Service -Name DoesNotExist
Figure 1.

Looking at different parts of the collection, we can see the last error that occurred, as showed by being the 0 index and the first error which is represented as -1 or in this case a 2 (remember that collections start at a 0 index). Knowing this, we can safely assume that the first item in the collection will talk about my service error that I threw and the last error will be related to a custom thrown error.

[Click on image for larger view.] Figure 2.

The beauty of the $Error object is that it is in fact, an object. We can take a look at the one of the errors and begin to see what it looks like and also, depending on what caused the error, maybe use the data within the error to log to a file. A great example is working with files and folders. Let's assume that I am performing a scan against the entire operating system drive (typically the C: drive) and I want to track any access denies that I happen to come across.

Get-ChildItem -Path c:\ -recurse -Force -ErrorAction SilentlyContinue |  Out-Null 

Ok, even though I am specifying the erroractionpreference value of silentlycontinue, all errors that occur will still be logged to $Error. After this has completed, we can see what paths gave us the most issues by looking only those errors that have returned access denied.

But how do we know where to look for this information? Well, we can take the first error and explore it by piping the output to something like Select-Object and specifying a * to view all of its properties.


$Error[0]  | Select-Object  -Property * 
[Click on image for larger view.] Figure 3.

Here we can see a lot of information with one such property being exception. This actually is an object (System.UnauthorizedAccessException) that we can test against to find all exceptions that deal with an unauthorized access exception that creates the access denied that we are seeing here.

Knowing this information, I can begin to filter out all  errors but those which have access exceptions.
$AccessDenied = $Error | Where {
$_.Exception -is [System.UnauthorizedAccessException]

Using the –Is operator is great way to test an object against a specific object type to determine if it is valid or not. Now we have filtered only the errors that we need and can work to further inspect the error and determine the best approach to only showing the path to the file or folder without spending a lot of time trying to parse the error using RegEx or something similar.

$AccessDenied[0]  | Select-Object  -Property * 

Again I will use Select-Object to fully display all of the properties within the error to determine if a property happens to meet what we are looking for.

[Click on image for larger view.] Figure 4.

Sure enough, our answer lies within the TargetObject property which happens to point to the full path of the folder that we had an issue with.


PS C:\WINDOWS\system32> $AccessDenied[0].TargetObject


Using this, we can quickly send all of the failed attempts to a file or folder to a log file and then have a list to review and take action on, if needed.

$AccessDenied | Select-Object -ExpandProperty TargetObject  | 

Out-File AccessIssues.txt

This isn't the end of the error object and its many, many uses. We can dig into another property, this time the InvocationInfo property, and determine where an error occurred within a script or function.

[Click on image for larger view.] Figure 5.

Looking at the line number and OffsetInline, we can quickly determine where in the script or function happened and then work from there to try and resolve it.

Errors can be annoying at times, but we can make use of the rich objects that PowerShell has to explore the error and better understand what happened and maybe even use it to quickly generate useful logs to make resolving an issue much simpler.

About the Author

Boe Prox is a Microsoft MVP in Windows PowerShell and a Senior Windows System Administrator. He has worked in the IT field since 2003, and he supports a variety of different platforms. He is a contributing author in PowerShell Deep Dives with chapters about WSUS and TCP communication. He is a moderator on the Hey, Scripting Guy! forum, and he has been a judge for the Scripting Games. He has presented talks on the topics of WSUS and PowerShell as well as runspaces to PowerShell user groups. He is an Honorary Scripting Guy, and he has submitted a number of posts as a to Microsoft's Hey, Scripting Guy! He also has a number of open source projects available on Codeplex and GitHub. His personal blog is at http://learn-powershell.net.

comments powered by Disqus
Most   Popular