Error Handling is a very important concept in every programming language including PowerShell which gives us several possibilities to manage errors in code.
In this article, I will try to cover as many as possible error handling concepts with examples from my practice and one of the important concepts is writing errors in external Error Log text file.
Table of Contents
Why Should We Bother Handling Errors In PowerShell
It is no fun to run any code or application full of errors and bugs as the matter a fact it is quite annoying so in order for users to have a pleasant experience handling the errors is one of the essentials in programming.
PowerShell is no exception to that way of thinking and programming ethics although it can take some additional coding and effort but trust me it is always worth it.
I would be more than happy to share my experience with error handling and even more satisfied if I can hear your tips from the field so we can all enrich our knowledge on this very important subject.
How To Handle And Log PowerShell Errors
Here is a general approach to error handling and logging errors into the external text file. I have an example in the next subheading that further explain each step so everything is much more understandable.
- Identify which Commands need Error Handling in your Function, CmdLet or Script.
- Set the ErrorAction parameter to value Stop for the command that needs Error Handling. This will stop executing the command when an error occurs and it will show the error message. Basically, we are making non-terminating errors into terminating in order to be able to handle the error in the catch block.
- Command that has ErrorAction parameter set to value Stop is wrapped in Try { } block. So when Error occurs handling of the code will jump from the Try block to Catch block.
- In Catch { } block that immediately follows after Try {} block, the error is handled by writing to the error log file on the disk.
- We use our own Write-ErrorLog CmdLet inside Catch{ } block to write the error in a text file on the disk. (See the explanation and code here)
- We have an additional switch error parameter to decide whether we want to write an error in the log file or not. This is totally optional.
- Use the Finally { } block if needed.
- Test the whole setup by intentionally breaking the code while in the development phase.
- Since some of the CmdLets calls are Scheduled we have routine to check external Error Log text file at least once a week and investigate errors that are caught. This step is part of improving the overall quality of the written code.
Example Of PowerShell Error Handling
To show you Error Handling and implement previously defined steps I will use my own Get-CPUInfo CmdLet which is in the Common module of the Efficiency Booster PowerShell Project. Efficiency Booster PowerShell Project is a library of CmdLets that help us IT experts in day to day IT tasks.
In order to follow me along, I highly encourage you to download the zip file with the source code used in this example.
Here is the location of Get-CPUInfo script:
…\[My] Documents\WindowsPowerShell\Modules\03common
Let’s use steps defined in the previous subheading to this example.
Step 1. I have identified the command that needs Error Handling in Get-CPUInfo CmdLet and that is a call to Get-CimInstance CmdLet.
Get-CimInstance @params
Step 2. So I have set up the ErrorAction parameter to the value ‘Stop‘ for Get-CimInstance CmdLet in order to force non-terminating errors into terminating and then to be able to handle such errors.
INFO: I use parameter splatting when running CmdLet. If you want to know more about parameter splating please read this article.
$params = @{ 'ComputerName'=$computer;
'Class'='Win32_Processor';
'ErrorAction'='Stop'}
$CPUInfos = Get-CimInstance @params |
Select-Object @{label="ServerName"; Expression={$_.SystemName}},
@{label="CPU"; Expression={$_.Name}},
@{label="CPUid"; Expression={$_.DeviceID}},
NumberOfCores,
AddressWidth
Step 3. Wrap up the call to Get-CimInstance CmdLet into the Try Block in order to be able to handle the error in a catch block that follows.
try {
Write-Verbose "Start processing: $computer - $env - $logicalname"
Write-Verbose "Start Win32_Processor processing..."
$CPUInfos = $null
$params = @{ 'ComputerName'=$computer;
'Class'='Win32_Processor';
'ErrorAction'='Stop'}
$CPUInfos = Get-CimInstance @params |
Select-Object @{label="ServerName"; Expression={$_.SystemName}},
@{label="CPU"; Expression={$_.Name}},
@{label="CPUid"; Expression={$_.DeviceID}},
NumberOfCores,
AddressWidth
Write-Verbose "Finish Win32_Processor processing..."
foreach ($CPUInfo in $CPUInfos) {
Write-Verbose "Start processing CPU: $CPUInfo"
$properties = @{ 'Environment'=$env;
'Logical name'=$logicalname;
'Server name'=$CPUInfo.ServerName;
'CPU'=$CPUInfo.CPU;
'CPU ID'=$CPUInfo.CPUid;
'Number of CPU cores'=$CPUInfo.NumberOfCores;
'64 or 32 bits'=$CPUInfo.AddressWidth;
'IP'=$ip;
'Collected'=(Get-Date -UFormat %Y.%m.%d' '%H:%M:%S)}
$obj = New-Object -TypeName PSObject -Property $properties
$obj.PSObject.TypeNames.Insert(0,'Report.CPUInfo')
Write-Output $obj
Write-Verbose "Finish processing CPU: $CPUInfo"
}
Write-Verbose "Finish processing: $computer - $env - $logicalname"
}
Step 4. When the error occurs in the try block it is handled in the Catch Block.
It is important to notice following in the catch block of code:
- Get-CPUInfo CmdLet switch parameter $errorlog has been used to decide whether to log the errors in an external text file or not. This is completely optional.
- Certain Error properties are collected using an automatic variable $_ ($PSItem is another name for the same variable). If you want to know more about which properties we collect please read here.
- Collected data about the error that will be handled has been passed to another CmdLet Write-ErrorLog that will write the data in an external text log file. Please read here about Write-ErrorLog CmdLet.
catch {
Write-Warning "Computer failed: $computer - $env - $logicalname CPU failed: $CPUInfos"
Write-Warning "Error message: $_"
if ( $errorlog ) {
$errormsg = $_.ToString()
$exception = $_.Exception
$stacktrace = $_.ScriptStackTrace
$failingline = $_.InvocationInfo.Line
$positionmsg = $_.InvocationInfo.PositionMessage
$pscommandpath = $_.InvocationInfo.PSCommandPath
$failinglinenumber = $_.InvocationInfo.ScriptLineNumber
$scriptname = $_.InvocationInfo.ScriptName
Write-Verbose "Start writing to Error log."
Write-ErrorLog -hostname $computer -env $env -logicalname $logicalname -errormsg $errormsg -exception $exception -scriptname $scriptname -failinglinenumber $failinglinenumber -failingline $failingline -pscommandpath $pscommandpath -positionmsg $pscommandpath -stacktrace $stacktrace
Write-Verbose "Finish writing to Error log."
}
}
Step 5. I have already mentioned that Write-ErrorLog CmdLet has been used to write the error data into an external text log file. Read more about this CmdLet here.
Step 6. I did not need Finally { } block for this example.
Step 7. In the development phase, I was intentionally breaking Get-CPUInfo CmdLet to test my error handling code.
Step 8. If Get-CPUInfo CmdLet is part of the scheduled code I would look regularly Error log file and work on correcting all the bugs in the code produced by Get-CPUInfo CmdLet.
Chain Of Events When PowerShell Error Occurs
Let’s talk a little bit about what is happening when an error occurs in PowerShell and which events are triggered one after another.
- Call to CmdLet is failing and error has just occurred.
- Since we have ErrorAction parameter set to value Stop our non-terminating error has forced into terminating error so the execution of code stops.
- The error that is failing in the CmdLet is written in the $Error automatic variable by the PowerShell.
- We have forced the error to be terminating in order to be able to handle with try catch finally block of error handling.
- Since our CmdLet call is wrapped in Try block and error is terminating PowerShell can trigger error handling looking for a Catch block.
- We can have several Catch blocks for one try block. If we have a Catch block that handles the actual error number that block is executed.
- Otherwise, PowerShell will look Catch block that handles all error numbers.
- Optionally in the Catch block, we can have code that will write the error in the external text Error log file.
- We can use the automatic variable $Error or $_ object to read Error data and write them in the external file.
- If there is no Catch block PowerShell will look for Catch block in parent call if we have nested calls.
- If there are no further Catch block or no Catch block at all that will handle error then PowerShell looks for Finally block to execute which is used to clean up resources as needed.
- After executing Finally block error message will be sent to the error stream for further processing.
In further sections of this article, you can read in more detail about the many terms mentioned (ErrorAction, $Error, Try Catch Finally, Terminating, Non-Terminating, etc ) in this bulleted list in order to better understand them.
How To Write PowerShell Errors Into The External Log File
Here I will explain the code of Write-ErrorLog CmdLet that writes error data that occurs in CmdLets and handle the error data into an external text file.
Error data are written in the file named Error_Log.txt in folder PSlogs.
Write-ErrorLog CmdLet is part of the Efficiency Booster PowerShell Project and if you want to download the source code of this CmdLet please click here.
Here is the location of Write-ErrorLog script which is part of the Utils module:
…\[My] Documents\WindowsPowerShell\Modules\02utils
Write-ErrorLog CmdLet Code
Here is the code of the whole Write-ErrorLog CmdLet.
<#
.SYNOPSIS
Writes errors that occur in powershell scripts into error log file.
.DESCRIPTION
Writes errors that occur in powershell scripts into error log file.
Error log file and error log folder will be created if doesn't exist.
Error log file name is Error_Log.txt and it has been saved into ..\Documents\PSlogs\
.PARAMETER hostname
Name of the computer that is failing.
.PARAMETER env
Environment where computer is located. For example: Production, Acceptance, Test, Course etc.
.PARAMETER logicalname
Type of the server that is failing. For example: Application, Web, Integration, FTP, Scan, etc.
.PARAMETER errormsg
Error message.
.PARAMETER exception
Error number.
.PARAMETER scriptname
Name of the powershell script that is failing.
.PARAMETER failinglinenumber
Line number in the script that is failing.
.PARAMETER failingline
Content of failing line.
.PARAMETER pscommandpath
Path to the powershell command.
.PARAMETER positionmsg
Error message position.
.PARAMETER stacktrace
Stack trace of the error.
.EXAMPLE
Write-ErrorLog -hostname "Server1" -env "PROD" -logicalname "APP1" -errormsg "Error Message" -exception "HResult 0789343" -scriptname "Test.ps1" -failinglinenumber "25" -failingline "Get-Service" -pscommandpath "Command pathc." -positionmsg "Position message" -stacktrace "Stack trace"
.EXAMPLE
Help Write-ErrorLog -Full
.LINK
Out-File
#>
Function Write-ErrorLog {
[CmdletBinding()]
param (
[Parameter(Mandatory=$false,
HelpMessage="Error from computer.")]
[string]$hostname,
[Parameter(Mandatory=$false,
HelpMessage="Environment that failed. (Test, Production, Course, Acceptance...)")]
[string]$env,
[Parameter(Mandatory=$false,
HelpMessage="Type of server that failed. (Application, Web, Integration...)")]
[string]$logicalname,
[Parameter(Mandatory=$false,
HelpMessage="Error message.")]
[string]$errormsg,
[Parameter( Mandatory=$false,
HelpMessage="Exception.")]
[string]$exception,
[Parameter(Mandatory=$false,
HelpMessage="Name of the script that is failing.")]
[string]$scriptname,
[Parameter(Mandatory=$false,
HelpMessage="Script fails at line number.")]
[string]$failinglinenumber,
[Parameter(Mandatory=$false,
HelpMessage="Failing line looks like.")]
[string]$failingline,
[Parameter(Mandatory=$false,
HelpMessage="Powershell command path.")]
[string]$pscommandpath,
[Parameter(Mandatory=$false,
HelpMessage="Position message.")]
[string]$positionmsg,
[Parameter(Mandatory=$false,
HelpMessage="Stack trace.")]
[string]$stacktrace
)
BEGIN {
$errorlogfile = "$home\Documents\PSlogs\Error_Log.txt"
$errorlogfolder = "$home\Documents\PSlogs"
if ( !( Test-Path -Path $errorlogfolder -PathType "Container" ) ) {
Write-Verbose "Create error log folder in: $errorlogfolder"
New-Item -Path $errorlogfolder -ItemType "Container" -ErrorAction Stop
if ( !( Test-Path -Path $errorlogfile -PathType "Leaf" ) ) {
Write-Verbose "Create error log file in folder $errorlogfolder with name Error_Log.txt"
New-Item -Path $errorlogfile -ItemType "File" -ErrorAction Stop
}
}
}
PROCESS {
Write-Verbose "Start writing to Error log file. $errorlogfile"
$timestamp = Get-Date
#IMPORTANT: Read just first value from collection not the whole collection.
" " | Out-File $errorlogfile -Append
"************************************************************************************************************" | Out-File $errorlogfile -Append
"Error happend at time: $timestamp on a computer: $hostname - $env - $logicalname" | Out-File $errorlogfile -Append
"Error message: $errormsg" | Out-File $errorlogfile -Append
"Error exception: $exception" | Out-File $errorlogfile -Append
"Failing script: $scriptname" | Out-File $errorlogfile -Append
"Failing at line number: $failinglinenumber" | Out-File $errorlogfile -Append
"Failing at line: $failingline" | Out-File $errorlogfile -Append
"Powershell command path: $pscommandpath" | Out-File $errorlogfile -Append
"Position message: $positionmsg" | Out-File $errorlogfile -Append
"Stack trace: $stacktrace" | Out-File $errorlogfile -Append
"------------------------------------------------------------------------------------------------------------" | Out-File $errorlogfile -Append
Write-Verbose "Finish writing to Error log file. $errorlogfile"
}
END {
}
}
#region Execution examples
#Write-ErrorLog -hostname "Server1" -env "PROD" -logicalname "APP1" -errormsg "Error Message" -exception "HResult 0789343" -scriptname "Test.ps1" -failinglinenumber "25" -failingline "Get-Service" -pscommandpath "Command pathc." -positionmsg "Position message" -stacktrace "Stack trace" -Verbose
#endregion
Write-ErrorLog CmdLet Explained
Let’s make our hand’s a little bit “dirty” and dive into PowerShell code.
In the BEGIN block we:
- Check if folder PSlogs exist in the (My) Documents folder of the current user.
- If the PSlogs folder doesn’t exist then create the folder.
- Check if file Error_Log.txt exists in the folder PSlogs.
- If Error_Log.txt doesn’t exist then create the file.
- Now we can move on to PROCESS block code.
BEGIN {
$errorlogfile = "$home\Documents\PSlogs\Error_Log.txt"
$errorlogfolder = "$home\Documents\PSlogs"
if ( !( Test-Path -Path $errorlogfolder -PathType "Container" ) ) {
Write-Verbose "Create error log folder in: $errorlogfolder"
New-Item -Path $errorlogfolder -ItemType "Container" -ErrorAction Stop
if ( !( Test-Path -Path $errorlogfile -PathType "Leaf" ) ) {
Write-Verbose "Create error log file in folder $errorlogfolder with name Error_Log.txt"
New-Item -Path $errorlogfile -ItemType "File" -ErrorAction Stop
}
}
}
In the PROCESS block:
- We format the line of text that we want to write into the log file.
- Then we pipe formatted text to Out-File CmdLet with the Append parameter to write that line of text in the file.
- We repeat the process of formatting the line of text and appending of that line to the Error log file.
PROCESS {
Write-Verbose "Start writing to Error log file. $errorlogfile"
$timestamp = Get-Date
#IMPORTANT: Read just first value from collection not the whole collection.
" " | Out-File $errorlogfile -Append
"************************************************************************************************************" | Out-File $errorlogfile -Append
"Error happend at time: $timestamp on a computer: $hostname - $env - $logicalname" | Out-File $errorlogfile -Append
"Error message: $errormsg" | Out-File $errorlogfile -Append
"Error exception: $exception" | Out-File $errorlogfile -Append
"Failing script: $scriptname" | Out-File $errorlogfile -Append
"Failing at line number: $failinglinenumber" | Out-File $errorlogfile -Append
"Failing at line: $failingline" | Out-File $errorlogfile -Append
"Powershell command path: $pscommandpath" | Out-File $errorlogfile -Append
"Position message: $positionmsg" | Out-File $errorlogfile -Append
"Stack trace: $stacktrace" | Out-File $errorlogfile -Append
"------------------------------------------------------------------------------------------------------------" | Out-File $errorlogfile -Append
Write-Verbose "Finish writing to Error log file. $errorlogfile"
}
$PSItem or $_
$PSItem contains the current object in the pipeline object and we use it to read Error properties in this case.
Just a quick explanation of each Error property from the $_ ($PSItem) automatic variable that we collect and write in Logfile:
- $_.ToString() – This is Error Message.
- $_.Exception – This is Error Exception.
- $_.InvocationInfo.ScriptName – This the PowerShell script name where Error occurred.
- $_.InvocationInfo.ScriptLineNumber – This is line number within the PowerShell script where Error occurred.
- $_.InvocationInfo.Line – This is the line of code within PowerShell script where Error occurred.
- $_.InvocationInfo.PSCommandPath – This is the path to the PowerShell script file on the disk.
- $_.InvocationInfo.PositionMessage – This is a formatted message indicating where the CmdLet appeared in the line.
- $_.ScriptStackTrace – This is the Trace of the Stack.
As you can see on the screenshot below we collect really useful information about the Error that we handle in the Logfile. The pieces of information are presented in very neatly fashion so we can immediately see:
- which error occurred,
- what were the message and exception,
- where the error occurred (script name, script location, line number and line of the code in the script)
- even the call stack is shown if needed.
Here are the final result and an example of one formatted error logged in Error_Log.txt file.
************************************************************************************************************
Error happend at time: 09/11/2019 18:20:41 on a computer: APP01 - -
Error message: The WinRM client cannot process the request. If the authentication scheme is different from Kerberos, or if the client computer is not joined to a domain, then HTTPS transport must be used or the destination machine must be added to the TrustedHosts configuration setting. Use winrm.cmd to configure TrustedHosts. Note that computers in the TrustedHosts list might not be authenticated. You can get more information about that by running the following command: winrm help config.
Error exception: Microsoft.Management.Infrastructure.CimException: The WinRM client cannot process the request. If the authentication scheme is different from Kerberos, or if the client computer is not joined to a domain, then HTTPS transport must be used or the destination machine must be added to the TrustedHosts configuration setting. Use winrm.cmd to configure TrustedHosts. Note that computers in the TrustedHosts list might not be authenticated. You can get more information about that by running the following command: winrm help config.
at Microsoft.Management.Infrastructure.Internal.Operations.CimAsyncObserverProxyBase`1.ProcessNativeCallback(OperationCallbackProcessingContext callbackProcessingContext, T currentItem, Boolean moreResults, MiResult operationResult, String errorMessage, InstanceHandle errorDetailsHandle)
Failing script: C:\Users\dekib\Documents\WindowsPowerShell\Modules\03common\GetCPUInfo.ps1
Failing at line number: 214
Failing at line: $CPUInfos = Get-CimInstance @params |
Powershell command path: C:\Users\dekib\Documents\WindowsPowerShell\Modules\03common\GetCPUInfo.ps1
Position message: C:\Users\dekib\Documents\WindowsPowerShell\Modules\03common\GetCPUInfo.ps1
Stack trace: at Get-CPUInfo<Process>, C:\Users\dekib\Documents\WindowsPowerShell\Modules\03common\GetCPUInfo.ps1: line 214
at <ScriptBlock>, <No file>: line 1
------------------------------------------------------------------------------------------------------------
TIP: If your scripts are scheduled in Task Manager the best practice is to have a routine of regularly checking the Error Log file and investigate the errors that occurred since the last check. I am doing this once a week.
Errors In PowerShell
There are two types of Errors in PowerShell:
- Terminating
- Non-Terminating
Terminating Errors
Here are the important features of Terminating errors:
- terminates execution of command or script
- triggers Catch block and can be error handled by the Catch block.
Examples are syntax errors, non-existent CmdLets, or other fatal errors
Non-Terminating Errors
Here are important features of Non-Terminating Error:
- A non-fatal error.
- Allows execution to continue despite the failure that just occurred.
- It doesn’t trigger the Catch block and cannot be Error Handled in the Catch block by default.
Examples are permission problems, file not found, etc.
How To Force Non-Terminating Errors Into Terminating
Use the ErrorAction parameter with value Stop to force non-terminating error into terminating as in the following example. The reason why we want to make non-termination error into terminating one is to be able to catch the error when occurs.
$AuthorizedUser = Get-Content .\Documents\WindowsPowerShell\Profile.ps1 -ErrorAction Stop
Basically, the workflow is as follows.
- When an error occurs,
- a non-terminating error has changed into terminating one since we have Stop value on ErrrorAction parameter,
- then since terminating error has occurred try block will send the error handling to catch block where error can be processed and
- optionally written to the external log,
- optionally error handling can continue in the final block.
NOTE: ErrorAction parameter overrides temporarily ErrorActionPreference variable while the call to CmdLet has been processed.
How To Treat All Errors As Terminating
We use the ErrorActionPreference variable to treat all errors as terminating by setting to the value Stop in the:
- script
- or session
Write the following line of code at the begging of the script to treat all errors as terminating.
$ErrorActionPreference = Stop
Type in Windows PowerShell Console the same command to setup terminating errors for the session.
ErrorAction Common Parameter
ErrorAction parameter belongs to the set of common parameters that we can use with any CmdLet. If we set CmdLetBinding on Advanced Functions than PowerShell automatically makes common parameters available for that command.
This is a parameter that I always use with CmdLets that need error handling since the ErrorAction Parameter determines how the CmdLet responds to a non-terminating error. It has several values that we will talk about in a minute but the value that I like to use is Stop and it is used to make non-terminating errors into terminating errors as written in previous sections.
The ErrorAction parameter overrides the value of the $ErrorActionPreference variable when applied to the specific command.
Here are valid values:
- Continue (Default)
- This is the default setting. Display error then continues execution.
- Stop
- Display error, and stop the execution.
- Inquire
- Displays error message and the user is asked to continue with execution.
- SilentlyContinue
- No error message is displayed and execution is continued. However, the error message is added to the $Error automatic variable.
- Ignore
- The same as SilentlyContinue, No error message is displayed and execution is continued. However, Ignore does not add an error message to the $Error automatic variable.
- Suspend
- This one is for workflows. A workflow job is suspended to investigate what happened, then the workflow can be resumed.
$ErrorActionPreference Preference Variable Explained
$ErrorActionPreference preference variable determines how Windows PowerShell responds to a non-terminating error (an error that does not stop the cmdlet processing) in a script, cmdlet or at the command line
If we want to override the value of the ErrorActionPreference preference variable for the specific command we use the ErrorAction common parameter as explained here.
The valid values for $ErrorActionPreference preference variable are:
- Continue (Default)
- This is the default setting. Display error then continues execution.
- Stop
- Display error message, and stop the execution.
- Inquire
- Displays error message and the user is asked to continue with execution.
- SilentlyContinue
- No error message is displayed and execution is continued. However, the error message is added to the $Error automatic variable.
- Suspend
- This one is for workflows. A workflow job is suspended to investigate what happened, then the workflow can be resumed.
Error Handling With Try/Catch/Finally Blocks
Try, Catch, and Finally, blocks are used to handle terminating errors in the scripts, functions, and CmdLets. A non-terminating error does not trigger Try block and Windows PowerShell will not look for Catch block to handle the error. So we need to force a non-terminating error to become terminating error using ErrorAction parameter with value Stop whenever we call some CmdLet or Advanced function.
Try block is used as part of the code that PowerShell will monitor for errors. The workflow in the Try block is as follows:
- Try block is the section of code that will be monitored for errors by Windows PowerShell.
- When the error occurs within Try block the error is saved to $Error automatic variable first.
- Windows PowerShell searches for a Catch block to handle the error if the error is terminating. If the Catch block has not been found in current scope Windows PowerShell will search for catch block in parent scopes for nested calls.
- Then the Finally block is run if exists.
- If there is no Catch block than the error is not handled and the error is written to the error stream.
One Try block can have several Catch Blocks that will handle different error types.
Catch block usually handles the error.
The Finally block is optional and can be only one. Usually, it is used to clean up and free the resources.
The syntax for Try, Catch, and Finally block:
try { }
catch [<error type>],[<error type>]
{ }
catch { }
finally { }
Getting Error Information With $Error Automatic Variable
$Error automatic variable is an array of error objects (both terminating and the non-terminating) that occurred in the current PowerShell session. The most recent error that occurred is written with index 0 in the array as $Error[0]. If we have just opened the Windows PowerShell session the $Error variable is an empty array and ready to be used.
Check the number of Errors in $Error variable with the following code:
$Error.Count
To prevent the error from being written in $Error automatic variable set ErrorAction parameter to value Ignore.
$Error variable is a rich object that has many useful properties worth reading and helpful for further understanding of the error that just occurred.
Let’s see some useful properties and in section Write-ErrorLog CmdLet Explained I have explained to you some useful examples of properties that are interesting to be written in an external log file.
$error[0] | Get-Member
$Error.CategoryInfo | Get-Member
$Error[0].Exception
$Error.InvocationInfo | Get-Member
Write-Error CmdLet
Write-Error CmdLet writes an object to the error stream.
Please read this article from Microsoft PowerShell documentation regarding this CmdLet.
Handling Errors from non-PowerShell processes
We can run applications from PowerShell script like for example, PsExec.exe or robocopy.exe and they are external processes for PowerShell. Since it is an external process, errors from it will not be caught by our try/catch blocks in PowerShell script. So how we will know whether our external process was successful or not.
Well, we can use the $LastExitCode PowerShell automatic variable.
PowerShell will write the exit code to the $LastExitCode automatic variable when the external process exits. Check the external tool’s documentation for exit code values but usually, 0 means success and 1 or greater values mean a failure.
Useful PowerShell Error Handling Articles
Here are some useful articles and resources: