Skip to end of metadata
Go to start of metadata

Introduction

  • PowerShell is a frequently used scripting language available for Linux, MacOS, Windows and other platforms.
  • JS7 offers the JS7 - PowerShell Module for simplified access to the JS7 - REST Web Service API
  • This article explains some common ways how to handle run-time errors in PowerShell job scripts.

Scenario

  • The below examples use a common scenario to raise a run-time error:
    • a workflow is assigned a variable lookup_path that specifies a directory that is tested in a later job to exist or not to exist.
    • the PowerShelll cmdlet Test-Path is used to test existence of the directory. The implication of the cmdlet is that
      • it returns $True if the directory exists and $False if it does not exist,
      • it raises an exception if the cmdlet parameter for the directory includes a $Null value.
  • In a perfect world script developers would check if a variable holds a null value before even using it. However, this world is far from being perfect. In addition, the script code required to perfectly check variables for null values would exceed by far the amount of code required for the effective processing of business logic. As a result, hardly anybody implements such checks.
  • The below examples can be executed by adding an order, for example from the JOC Cockpit Workflows view in two variants:
    • add the order without modification of the lookup_path variable: the example will run without error,
    • add the order and modify the value of the lookup_path variable to an empty string: an error will be raised:

      x

PowerShell Profiles

PowerShell profiles can be used to modify defaults available with PowerShell.

  • A PowerShell profile will be used if available from one of the locations offered by the respective platform (Windows, Linux, MacOS etc., please check applicability of profiles for your platform).
  • You can examine the global $PROFILE variable to verify applicable profile locations:
    • PS C:\> $PROFILE | Get-Member
    • Profile locations typically are applied from the following properties in descending order. This includes that all profiles from any locations are applied if available.

      • $PROFILE.AllUsersAllHosts
      • $PROFILE.AllUsersCurrentHost
      • $PROFILE.CurrentUserAllHosts
      • $PROFILE.CurrentUserCurrentHost
  • JS7 by default does not make use of a specific PowerShell profile, instead the above order of available profiles applies.

No Error Handling

This situation boils down to defaults available with PowerShell.

  • Built-in defaults of PowerShell apply.
  • Defaults that are modified by a PowerShell profile.

Continue Processing in case of Errors

The global $ErrorActionPreference variable makes use of the default value continue. As a result

  • the job script reports error messages to the stderr channel,
  • the job script continues to run after being hit by an error.

This is the most undesirable scenario as it implies that the PowerShell script

  • continues and might perform very bad things, for example if a cmdlet such as Remove-Item -Path /$env:LOOKUP_PATH -Recurse would be used with $env:LOOKUP_PATH evaluating to null.
  • completes with an exit code 0 signaling success if the last cmdlet executed with the script was successful. JS7 cannot know that an error occurred and users will not be notified.

Find the below example for download: pdErrorPowerShell_01_No_Handling_Continue.json

Example how not to use error handling
#!/usr/bin/env pwsh

$lookupPath = $env:LOOKUP_PATH
if ( !$lookupPath )
{
  $lookupPath = $null
}

If ( Test-Path -Path $lookupPath -PathType container )
{
	Write-Host "path found: $env:LOOKUP_PATH"
} else {
	Write-Host "path not found: $env:LOOKUP_PATH"
}

Stop Processing in case of Errors

The global $ErrorActionPreference variable in this example is set to the value stop. As a result

  • the job script reports error messages to the stderr channel,
  • the job script immediately stops after being hit by an error.

This is a somewhat acceptable scenario as it prevents the PowerShell script to continue and raises an exception that is considered by JS7 that will fail the job.

Find the below example for download: pdErrorPowerShell_01_No_Handling_Stop.json


Example how use PowerShell global error handling
#!/usr/bin/env pwsh

# global setting: stop execution in case of errors
$ErrorActionPreference = "stop"

$lookupPath = $env:LOOKUP_PATH
if ( !$lookupPath )
{
  $lookupPath = $null
}

If ( Test-Path -Path $lookupPath -PathType container )
{
	Write-Host "path found: $env:LOOKUP_PATH"
} else {
	Write-Host "path not found: $env:LOOKUP_PATH"
}

Error Handling

This situation boils down to defaults available with PowerShell and to the handling of errors with jobs.

  • Built-in defaults of PowerShell apply.
  • Defaults that are modified by a PowerShell profile.
  • Error detection by JS7, returning exit codes and controlling exceptions.

Use Error Detection by JS7

The global $ErrorActionPreference variable can have any value. For reasons explained above it is recommended to set the variable to the value stop.

In addition, JS7 error detection is activated: any output to the stderr channel is considered to signal the job being failed. This is achieved by use of the respective setting in the "Job Options" tab of the property editor in the Workflows view:


As a result

  • the job script can report errors by use of the Write-Error cmdlet,

  • the global $ErrorActionPreference variable determines if processing is stopped or is continued,
  • errors are reported and notified by JS7.


Find the below example for download: pdErrorPowerShell_02_WriteError.json


Example how to make JS7 detect PowerShell errors
#!/usr/bin/env pwsh

# use of Write-Error cmdlet
# consider the job options to fail if output to stderr is detected

$lookupPath = $env:LOOKUP_PATH
if ( !$lookupPath )
{
  Write-Error "empty lookup path not accepted"
}

if ( Test-Path -Path $lookupPath -PathType container -ErrorAction stop )
{
	Write-Host "path found: $env:LOOKUP_PATH"
} else {
	Write-Host "path not found: $env:LOOKUP_PATH"
}

Set Exit Code

Setting a non-zero exit code in a PowerShell script performs two operations:

  • it stops processing of the script,
  • it forwards the exit code to JS7.

This works independently from setting the global $ErrorActionPreference variable to a specific value and to error detection from the stderr channel by JS7.

Find the below example for download: pdErrorPowerShell_03_Exit.json


Example how to exit a PowerShell script with an exit code value
#!/usr/bin/env pwsh

# use of Write-Error cmdlet
# exit script with an exit code 

$lookupPath = $env:LOOKUP_PATH
if ( !$lookupPath )
{
  Write-Host "empty lookup path not accepted"
  Exit(12)
}

if ( Test-Path -Path $lookupPath -PathType container -ErrorAction stop )
{
	Write-Host "path found: $env:LOOKUP_PATH"
} else {
	Write-Host "path not found: $env:LOOKUP_PATH"
}

Control Exceptions with Try/Catch

Use of the PowerShell Try/Catch instructions offers full control of error handling in case of errors:

  • it stops processing of commands in the Try block,
  • it allows to handle errors in the Catch block that should not prevent further execution of the script,
  • it allows to handle errors in the Catch block that prevent further execution of the script, e.g. by use of the Write-Error cmdlet that raises an exception that is forwarded to JS7.

This works independently from setting the $ErrorActionPreference variable to a specific value and to error detection from the stderr channel by JS7.

Find the below example for download: pdErrorPowerShell_04_Try_Catch.json


Example how to use try/catch to control PowerShell script exceptions
#!/usr/bin/env pwsh

$lookupPath = $env:LOOKUP_PATH
if ( !$lookupPath )
{
  $lookupPath = $null
}

try {
	If ( Test-Path -Path $lookupPath -PathType container )
	{
		Write-Host "path found: $env:LOOKUP_PATH"
	} else {
		Write-Host "path not found: $env:LOOKUP_PATH"
	}
} catch {
    $message = $_.Exception | Format-List -Force | Out-String
    Write-Error "error occurred: $message"
}

Further Resources


  • No labels
Write a comment…