Handling errors in Powershell using try and catch
The basic structure of a try catch
So, what is a try catch
?
A Try statement contains a Try
block, zero or more Catch
blocks, and zero or one Finally
block. A Try
statement must have at least one Catch
block or one Finally
block.
So, the Try
block is where we put our code, the Catch
block is where we handle errors and the Finally
block is where we do actions like log the running of a function.
Let’s first try (eeyy) something we know will work, like a simple Write-Host
function Test-TryCatch {
Try {
Write-Host "Hey mom look at me"
}
Catch {
Write-Host $_
}
}
If we run this code, it outputs the following.
Hey mom look at me
Just like expected.
Now, let’s try something we know will fail, like finding a service that doesn’t exist with Get-Service
.
function Test-TryCatch-v2 {
Try {
Get-Service "thisisjustabogusservicedesignedtogivemeanerror" -ErrorAction Stop
}
Catch {
Write-Host $_
}
}
Let’s first try running only the the line including Get-Service
on it’s own.
C:\temp $ Get-Service "thisisjustabogusservicedesignedtogivemeanerror"
Get-Service : Cannot find any service with service name 'thisisjustabogusservicedesignedtogivemeanerror'.
At line:1 char:1
+ Get-Service "thisisjustabogusservicedesignedtogivemeanerror"
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (thisisjustabogu...togivemeanerror:String) [Get-Service], ServiceCommand
Exception
+ FullyQualifiedErrorId : NoServiceFoundForGivenName,Microsoft.PowerShell.Commands.GetServiceCommand
This produces an error, but it’s quite alot and hard to read. Now, let’s try running our function.
C:\temp $ Test-TryCatch-v2
Cannot find any service with service name 'thisisjustabogusservicedesignedtogivemeanerror'.
As you can see, it outputs only the first line of the error, making it more readable. We can also create custom messages if we so desire.
Handling errors
Ok, so we’ve got the basics of the try catch down, let’s move onto handling known errors. I’ve chosen to work with the xbgm
service
function Test-TryCatch-v3 {
#Define a service that exists on your system
$service = "xbgm"
Try {
$service_status = (Get-Service $service -ErrorAction Stop).Status
if($service_status -eq "Stopped"){
Start-Service $service -ErrorAction Stop
}
elseif ($service_status -eq "Running") {
Write-Host "Service $service is already running"
}
}
Catch {
Write-Host $_
}
}
Now, if I run this it should fail (I’ve chosen a service I know will fail, so cheating a bit).
C:\temp $ Test-TryCatch-v3
Service 'Xbox Game Monitoring (xbgm)' cannot be started due to the following error: Cannot open xbgm service on computer '.'.
So let’s handle that error! If we know a scenario where our code may fail we can handle it gracefully. We can also see if we can retry it using a do until
, but that’s for another post.
This time we want the following:
- Handling known errors
- Handling of unknown errors
- Pause the script if we get unknown errors
Let’s take our v3 function and upgrade it a bit!
function Test-TryCatch-v4 {
#Define a service that exists on your system
$service = "xbgm"
Try {
$service_status = (Get-Service $service -ErrorAction Stop).Status
if($service_status -eq "Stopped"){
Start-Service $service -ErrorAction Stop
}
elseif ($service_status -eq "Running") {
Write-Host "Service $service is already running"
}
}
Catch {
if ($_ -like "Service 'Xbox Game Monitoring (xbgm)' cannot be started due to the following error: Cannot open xbgm service on computer '.'.") {
Write-Host "The chosen service cannot be started on this computer due to missing depencies - this is expected!"
}
else {
Write-Host "ErrorMessage: $_"
Pause
}
}
}
This way, if we run our function it should output our known error and let the user know what is happening and why!
C:\temp $ Test-TryCatch-v4
The chosen service cannnot be started on this computer due to missing depencies - this is expected!
For the last part of the try catch
we finally come to the last part, the finally
block. This must be defined straight after the catch
and runs no matter what happens - error or not!
So let’s make that function into a v5!
function Test-TryCatch-v5 {
#Define a service that exists on your system
$service = "xbgm"
Try {
$service_status = (Get-Service $service -ErrorAction Stop).Status
if($service_status -eq "Stopped"){
Start-Service $service -ErrorAction Stop
}
elseif ($service_status -eq "Running") {
Write-Host "Service $service is already running"
}
}
Catch {
if ($_ -like "Service 'Xbox Game Monitoring (xbgm)' cannot be started due to the following error: Cannot open xbgm service on computer '.'.") {
Write-Host "The chosen service cannot be started on this computer due to missing depencies - this is expected!"
}
else {
Write-Host "ErrorMessage: $_"
Pause
}
}
Finally {
#Show the user how long until the new Game of Thrones season
[datetime]$Countdown="04/14/2019 03:00"
$data = ($Countdown-(Get-Date))
Write-Host "Script ran... but did you know that it's" $data.Days "days" $data.Hours "hours" $data.Minutes "minutes until the new Game of Thrones season??" -ForegroundColor DarkGreen
}
}
And this is the output.
C:\temp $ Test-TryCatch-v5
The chosen service cannot be started on this computer due to missing depencies - this is expected!
Script ran... but did you know that it's 23 days 16 hours 50 minutes until the new Game of Thrones season??
Using try catch finally
you can build more robust functions and handle errors better.