Saturday, 18 February 2017

Error handling PowerShell

Here are some good things to know about error handling, first there is no one way to do it.  You can use a single file, or many files, log it just to file or output on the terminal, event log email it etc, now I'm going to skip the email one simply because I don't believe that is the best way to use error handling and I get enough mail already.

First of all for testing your error handling you need to create an error, so for this, I have a simple script that gets info from localhost1 since there is no localhost1 it will error.

$Time=Get-Date
{
    Get-EventLog -CN SKB5223 -logname security -After
    (Get-Date).AddDays(-3) | Where-Object
    {($_.InstanceID -eq 4634) -or
    ($_.InstanceID -eq 4624)} |
    Select-Object Index,TimeGenerated,InstanceID,Message | out-host
}

We are using the Try command and then encapsulate the command we are running that way when it fails we can use the catch statement to collect what happened.

Now the outcome of the script above is an error saying localhost1 network "Operation Error The network path was not found." so we know we have an error, and I can fix that by removing the computer name and have it run local or put a good name there, however for the moment lets keep it as is so that we might better test the error handling.

Now as with all things there are many things we can do with the output from the error, let's say I want to log the error to command line output, so I can catch the error.

Catch
{
Write-host -ForegroundColor Red "ERROR HAS HAPPENED"
}

How about logging the error to file, this is almost the same as it uses the catch to collect the output but we need to also define where the logfile will be now since I most likely will have more than one command and therefore will want to reuse the logfile without specifying its location each time I'll use the $ErrorLog variable to store the location.

I'm also going to have two log locations one for error and one for warnings, this way I can keep logging information and errors separate.

##Setting Error Log Location
$ErrorLog = "Error.log"
$WarningLog = "Warning.log"

###
#Script block goes here
###

Catch
{
"$Time ERROR $_" | Add-Content $ErrorLog
}

So we will now look at the dialog box, this is not the most common these days however can still be good if you want to make sure user sees the error when it runs, how I would say these days not to use this as it puts a hold in your script and makes it hard to run it automatically.

Catch
{
$wshell = New-Object -ComObject Wscript.Shell
$wshell.Popup("Operation Error $_",0,"Error",0)
}

If you are using scripts allot and want to know when they ran and when they error, perhaps even using powershell to check the status then writing to the event log might be best for you.
However they are maybe the longest from the point of view that you need to define the even and its error id and log that it will be placed into.

Catch
{
Write-EventLog -EventId 8888 -LogName 'Windows PowerShell' -Message "The Script has incountered and ERR $_ This prevents script finishing" -Source PowerShell -EntryType Error
}

So now lets see all of those in one script and how it might look, if we put them all into one script, below shows all of the example from above with the comments added so that you can see how it might looks with one extra that we haven't covered yet, the break command, that is used when you don't want to continue with next action in the script.

$Time=Get-Date
##Setting Error Log Location
$ErrorLog = "Error.log"
$WarningLog = "Warning.log"

##Write beginning
write-host -ForegroundColor Red "STARTING TO GET SECURITY LOG $Time"

#Trying command
try
{
    Get-EventLog -CN SKB5223 -logname security -After
    (Get-Date).AddDays(-3) | Where-Object
    {($_.InstanceID -eq 4634) -or
    ($_.InstanceID -eq 4624)} |
    Select-Object Index,TimeGenerated,InstanceID,Message | out-host
}

#If error capture
Catch
{
#write error to file
"$Time ERROR $_" | Add-Content $ErrorLog

#write error to eventlog
Write-EventLog -EventId 8888 -LogName 'Windows PowerShell'
-Message "The Script has incountered and ERR $_ This prevents script finishing"
-Source PowerShell -EntryType Error

#write error to console
Write-host -ForegroundColor Red "ERROR HAS HAPPENED"

#write error to popup
$wshell = New-Object -ComObject Wscript.Shell
$wshell.Popup("Operation Error $_",0,"Error",0)

#stop script
exit
}

Since I raised the point about break I should also show you another way to handle this within the command using the "-ErrorAction Stop" however the break makes it easier not to forget.

You can also choose to take an action as part of the error handling, for example, reboot server or any number of other actions.