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
Show More Show Less 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

