Master PowerShell Errors with Try Catch Blocks
17K views
Mar 13, 2024
Take your PowerShell script and function writing to the next level by managing your errors using try catch blocks! Read the article here: https://jeffbrown.tech/using-exception-messages-with-try-catch-in-powershell/ Chapters: 00:00 Introduction 00:11 Try Catch Syntax 01:48 Try Catch Example 02:55 Using -ErrorAction 04:13 Capturing Error Messages 06:07 Multiple Catch Blocks 08:30 Finding Exception Messages 11:26 Adding logic to Catch block 13:49 Summary
View Video Transcript
0:00
What's up everyone, today I want to talk to you about how to handle errors inside your script and functions with using try-catch blocks
0:08
Let's go ahead and get started. You've written a script or function and it works great most of the time
0:15
but every now and then it's going to run into an error or something unexpected that you didn't know was going to happen
0:21
How do you handle those errors and possibly accommodate for them, and then make sure what it outputs onto the screen is useful information
0:30
One way you can handle this is using try-catch blocks. Try-catch blocks do exactly that, they try something and then they catch any errors
0:38
Let's jump over to VS Code and take a look at the try-catch syntax
0:42
We have try and some curly brackets, and inside the try block there you're going to put in one or more commands that you want to try
0:51
I would suggest keeping it at typically one command, or you can group similar commands together that might be doing something like enabling a user and maybe setting some properties on it
1:01
But you don't want to put your entire script in here, and you can have multiple try-catch blocks inside your script or function to handle multiple commands or scenarios
1:11
and then have separate custom ones for each. We're going to try one or more commands
1:17
and then we have a catch block, and that's what you want to do with your terminating error
1:21
Maybe you want to try it again, or set a specific value, or write something out to a log file
1:28
Maybe you're keeping track of what you're wanting to do with that particular script or function
1:33
You're just going to handle the error and continue on with the script
1:37
Let's take a look at how you can write a try-catch block, and then also use exception messages, which we'll talk about later, in order to customize your catch scenarios
1:47
First, let's generate an error. I have a command here of new item
1:52
I've given it a path that does not exist on my local system
1:56
and we're just going to create an empty text file here. Let me highlight this line
2:02
Okay, there we go. We've got an error here. It just says could not find part of this path
2:09
Maybe you have this in a script, and if that path doesn't exist, maybe you want to catch it and create that path, and then attempt to create the file again
2:18
But right now, we've generated an error. Let's see how we can handle this using try-catch blocks
2:24
Here we have a try-catch block with our command in it, and then we're catching it
2:28
and we're just going to write a warning out to the screen saying something went wrong
2:33
We're not going to handle it or anything. We're just customizing our error message here a little bit
2:38
I'll highlight this and run this try-catch block. Now, I want you to notice that we didn't see our message here of something went wrong
2:49
It does highlight line 2, our command saying it could not find part of that path
2:55
One thing you might run into when doing try-catch blocks is it only goes into the catch part of it
3:01
if the command you tried to run is terminating, meaning it's fatal or it's really bad potentially
3:07
In this case, this is not one of those things. In order to go into our catch block, we have to add a parameter to our command
3:17
which should be available for most commandlets that people write, and that is error action
3:22
We have to tell it what to do on error action, and we're going to tell it to stop
3:26
I have another example here with this update. Right here at the end, you can see we have dash error action stop
3:32
We are telling this if it runs into an error, we're going to stop what we're doing and go into our catch block
3:40
Let's actually clear out the screen a little bit, and we'll highlight this part again
3:47
There we go. Now we've got it to successfully go into our catch block by telling it to stop on error action
3:53
It goes in there, and we see our warning message of something went wrong
3:58
Now that we have our try catch block successfully erroring out and going into our catch block
4:05
let's make it a little more useful. Right now we're saying just something went wrong, not very helpful
4:11
In our next example, we are going to output the specific error message
4:17
One way you can do this is using the $error automatic variable
4:22
This is not a variable that you have to define. It's a system reserved one, and it's an array or a list of all the previous errors that have happened during your current session
4:34
Again, we have try, we have our command in there, error action stop. Inside of our catch block, we're doing right warning, and in the message parameter, we're doing $error
4:43
We have zero there in square brackets, meaning we're looking at the first item in the array
4:49
which is the newest error that just occurred. Let's take this and run it. Perfect
4:58
This is pretty much what we saw earlier, except it's not in red text or anything like that
5:03
but warning could not find part of the path. This is how you can specifically put out just the error message that occurred whenever you tried this command
5:15
Let's go ahead and clear out the screen. You can also access this error message using $ underscore
5:23
That just means the current object that's in your pipeline or that has been occurring here
5:28
and in this case, it's what's coming out of your try block
5:32
Basically, you're running your command, it fails, and inside the catch block, you just have $ underscore
5:39
and that is the current object that you're working with, which is the error message
5:45
You can take this and save it into a variable. I usually do $ message
5:49
You can do $ error message, whatever makes most sense for you
5:53
and then write out the warning using the variable. Again, this should give us the exact same thing we saw a second ago
5:59
just showing you different ways you can access that error message that has occurred with your command
6:04
inside your try block. Now, in all of our examples so far, we've had one catch block
6:09
and it's just all-encompassing of what has errored out in our script here
6:14
but you can have multiple catch blocks to deal with specific error message that occurs
6:21
Let's take a look at an example of that. Again, we have our try block
6:28
I have two different commands in here. We're going to look at this two different ways
6:32
The first one is new item. We've already seen this one where the path doesn't exist
6:36
and we have multiple catch blocks. Notice in the catch blocks between the catch and the start of the catch block in the curly brackets
6:44
we have different exception messages. We have not supported, meaning an illegal character was used in the file name
6:51
or the directory was not found. We have a general input output exception
6:56
Then right here at the end, we have a catch without any exception messages in it
7:02
If it doesn't match any of the ones above, this is a catch-all, basically
7:07
of if it doesn't match any of those, then we have a generic message here where I just said an unexpected error occurred
7:13
On line 54, we're going to run this example first. We know this is where the path does not exist
7:21
We'll highlight all of this. Let's go ahead and run it. We see right down here, warning, the path is not valid
7:31
That went into our directory not found exception message there. The path is not valid
7:40
We'll clear out the screen again. I'm going to comment out this one, and we're going to take a look at example number 2 here
7:46
where I have used an illegal character in the file name of the asterisk or the wildcard here
7:52
Interesting, when I originally wrote this in a blog post many years ago
7:56
and probably using PowerShell 5, this would catch on the not supported exception
8:02
showing warning, illegal character used in the file name. Here in PowerShell 7, or maybe it's Windows 11, it's now just a generic IO exception
8:11
On my message here on line 66, I just put illegal character used in file name
8:16
If you're looking at my blog post that goes along with this, that's why you're seeing it this way
8:22
Let's go ahead and take this, and we'll run it. There we go, illegal character used in the file name
8:31
You're probably thinking this is great, but how do you find these exception messages
8:35
so you can customize your catch blocks and handle different errors in different ways
8:39
We have a couple of different ways of doing that. What you need to do is generate the error again, so we'll do this one
8:47
We're going back to path not found. You can use the $error automatic variable, and it has some additional properties on it
8:54
in order to get the exception. You can do .exception, .gettype, method, and look at the full name of the exception
9:02
Let's go ahead and run this. You can see right here we have system.io.directory not found exception
9:09
which if we scroll back up, that's exactly what we used right here
9:13
directory not found exception on line 62. That's one way to find your exception messages
9:20
That's probably the quickest and easiest and most consistent way because I want to show you something here
9:26
I used to do this, and I've written scripts using this, but for some reason
9:30
I was preparing for this demo, and this wasn't working. It's very weird. If you know why, leave me a comment down below
9:36
But PowerShell 7 introduced a new commandlet called geterror where you can get errors that have occurred in your session
9:43
basically pretty much the same as the $error automatic variable. What you can do is run geterror, and there is a parameter for newest
9:52
so you can get the newest one error that has occurred. Let's go ahead and run this
9:58
It puts out a lot of information if we scroll back up
10:02
One thing you'll notice is exception type. We have right here system.io directory not found exception
10:09
same thing we just saw in our earlier example. The one thing that was not working for me earlier
10:16
I often use line 83 to get the latest exception type. If I run that now, it is actually returning back the exception message here
10:25
I was having an issue where it wasn't for some reason, but your mileage may vary. I've rebooted since then
10:32
Maybe that changed something. I don't know. But this is another way for using PowerShell 7 to get that exception message
10:38
Then one thing here you can do, oh, this is where I was having the issue, was saving the last error message
10:43
In our examples earlier, I showed you how to access the error message using the error automatic variable or just underscore or just dollar sign underscore
10:51
for the current item in the pipeline. Another way you can do that is using get error
10:55
Again, we're looking at the newest error, and we can do .exception.message
11:01
I would often save this to my own variable called last error
11:06
If you run that, and then we take a look at last error, it will show us could not find part of the path, exactly what we've been seeing before
11:14
Earlier we took a look at finding that exception messages, having multiple catch blocks, and dealing with those specific exception messages
11:26
But sometimes those exception messages are very generic, and they might encompass a lot of different scenarios
11:32
One of those that I've ran into is if you want to use the Microsoft Graph cmdlets
11:37
and add a user to a group, if they're already a member of that group
11:41
you get a very generic exception message. So this is another way you can deal with that inside of your catch block
11:50
I have my command here. I'm going to try to add this user to this group here
11:55
And we get back this message saying one or more object references
11:59
already exist for the properties members. A very long message. It's a warning, or actually that's an error message
12:06
But maybe you don't necessarily care if they're already a member of the group. I was doing this when I was adding users to groups on a mass scale
12:14
and I was logging it out to a file, and I just wanted to make note of that
12:18
It's like, hey, I don't care if they're already a member of the group, I'm just going to write an info message to my log file that they're already a member of it
12:26
If we go back and take a look at what our exception message is, it's very generic, system.exception
12:32
That could encompass a lot of different things when we run our command here. What I've done here, we're going to try it here
12:39
New group member, we know that's going to fail. And inside my catch block, I'm saving the last error message to a message variable
12:48
that I'm creating, and I'm just using an if statement saying, hey, if the message is like that one showing the person is already a member
12:56
of the group, one or more added object references already exist, I'm just going to output to the screen that they're already a member of the group
13:03
We're not going to make it a terminating error or anything. We're not going to stop the script or do anything about it
13:09
I don't care. I'm just going to lock it to the screen or maybe to a log file
13:13
If our message is not like that, then let's write out our warning because something else has occurred here
13:19
Let's highlight this and just show you what that looks like. And there we go
13:25
We just have info user is already a member of the group. No harm, no foul
13:30
Just another way I've dealt with error messages inside my catch block if I don't particularly care about that message or I'm not going to do anything
13:37
about it or I'm just logging. What this is really showing is inside your catch block here
13:42
you can write however much you need to or want to to handle your different errors
13:47
and do different things with them. That's it for this video. Hopefully this helps you in taking your script or function writing
13:54
to the next level. Make sure you're handling your error messages and try to encompass
13:59
every scenario that you can. Of course, you always find new ones as you use the script more
14:04
or get it into different environments. Thank you for watching, and we'll see you next time
#Programming