Using the PowerShell Definition Type

Windows PowerShell is a shell meant for interactive use, but it can also be used for non-interactive scripts. It comes standard with Microsoft Server 2008 and later.

Note: You must assign at least one Process Server to run PowerShell processes in order to use this Definition Type.

32-Bit and 64-Bit Interpreters

Windows offers two versions of the PowerShell interpreter:

  • 32-bit: %SystemRoot%\SysWOW64\WindowsPowerShell\v1.0\powershell.exe

  • 64-bit: %SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe.

Note: On 64-bit Windows platforms, the System32 directory contains 64-bit binaries and SYSWOW64 is the directory containing 32-bit binaries.

Note: When setting interpreter paths in RunMyJobs, you can use either forward slashes or backslashes.

By default, the PowerShell Definition Type uses the 32-bit interpreter. However, there are multiple ways to specify which PowerShell interpreter is used.

  • The /configuration/jcs/PlatformAgent/WindowsLocalInterpreterBits registry entry lets you set the overall default by specifying 32 or 64. Alternatively, you can set this entry to the path of the desired PowerShell executable (see above). If this registry entry is not present, the 32-bit interpreter is the overall default.

  • You can use the Process Server Parameters listed below to override the default interpreter on a given Process Server.

  • You can use Process Definition Parameters to override both the registry entry and any Process Server Parameters.

Process Server Parameters

The relevant Process Server Parameters are:

  • LocalInterpreter_PS1. You can set this Parameter to 32 for 32-bit, or 64 for 64-bit. Alternatively, you can set this Parameter to the path of the desired PowerShell executable (see above).
  • InterpreterWhitelist_PS1: You can set this Parameter to a regex expression that matches all interpreters you want supported. Use *. to allow any, or (for example) C:/Windows/.*/powershell.exe to allow any powershell.exe in a subfolder of C:/Windows. You can also explicitly set two interpreters, separated by a comma without a space. For example: %SystemRoot%/system32/WindowsPowerShell/v1.0/powershell.exe, %SystemRoot%/SysWOW64/WindowsPowerShell/v1.0/powershell.exe. If you set this Parameter to an invalid value such as foo, no PowerShell interpreter will run because no interpreters match.

Process Definition Parameters

To override both the registry setting and the Process Server Parameters, you can set the JCS_INTERPRETER_PS1 Process Definition Parameter to the path of the desired interpreter.

Execution Policy

The default execution policy for PowerShell is Restricted, which does not allow the execution of scripts. As of version 9.0.2, RunMyJobs calls the PowerShell scripting engine in a mode that enables scripting for that session only, making it no longer necessary to set the execution policy beforehand.

Background and Foreground Processes

By default, commands are executed in the background. If you want to display a process on the screen, use the {session} or {console} keywords in the Run As User field, followed by the credential or username and password.

  • {session}: RDP, Windows Terminal Server, or console session.
  • {console}: Console session only. Console sessions are displayed on the physical monitor (if any) attached to the server.

Note: The specified user must be logged in to a console session (for {console}) or any type of supported session (for {session}) or the Process will enter status Error.

Note: Sessions are always for a user account. It is not possible to run session/console Processes under NT Authority\LocalSystem.

Examples

This example shows how to configure a Run As User field with {session} and a virtual user:

{session}{virtual}:ops

This example shows how to configure a Run As User field with {console} and a credential:

{console}example@example.corp

Variables and Parameters

You can manipulate Parameters in a PowerShell script using the standard PowerShell $VARIABLE syntax. You can also use get-variable -name <name> -scope global.

The PowerShell language has knowledge of data types. The String and Number data types are supported. DateTimeZone Parameters are stored in DateTime variables. This means that you cannot transfer the time zone of a Parameter to a PowerShell variable, because it will be converted to the corresponding date and time in the local time zone of the Windows system. Out Parameters are converted to UTC (GMT).

You can specify Out Parameters with the $VARIABLE = VALUE syntax or the Set-Variable, New-Variable cmdlets.

Array Parameters are supported in PowerShell, but are limited to unique elements only.

Error Handling

If a script exits with an error, this is correctly reflected in RunMyJobs. Output Parameters are set as far as they were already set.

The PowerShell process modifies ErrorActionPreference. The default value for this Parameter in Redwood Server is "Stop". If you want a script to continue on error, add this line to the beginning of your script:

$ErrorActionPreference = "Continue".

Apartment Models

Windows has a concept of an "apartment model". This is a remnant of older Windows versions where it was normal that COM and OLE objects were not multithreaded, and would not be usable from a multithreaded application. To get around this, Microsoft developed a set of shims that allow multithreaded applications such as PowerShell to funnel all access to COM and OLE objects through a single thread. This is called the Single Thread Apartment model. Depending on which commandlets and COM/OLE objects you call, you may need to have the PowerShell started either in Single Thread Apartment (STA) or Multi Thread Apartment (MTA) mode.

RunMyJobs starts PowerShell in its default mode. For v1 and v2 of PowerShell this is MTA. For v3 it is STA. Note that Windows PowerShell ISE is always started in STA mode.

If you want to use an explicit mode, set the LocalInterpreter_PS1 Process Server Parameter to powershell -sta or powershell -mta.

Examples

Parameters

This example shows how to pass numeric (N1), string (S1) and date (D1) Parameters to and from PowerShell scripts.

Copy
$N1=$N1 + $N1
# Concatenate string
$S1="$S1 $S1"
[datetime]$DTEMP="2023/07/27 23:59:59Z"
echo "You said $D1, I prefer $DTEMP"
$D1=$DTEMP

Error Out

This example shows how to return a non-zero exit code, resulting in the Process going into Error status.

Copy
$N=1
echo "Exiting with value $N."
if ($N -gt 0)
{
  [Environment]::Exit($N);
}

echo "Not reached"

Rename Files

This example shows how to rename all of the files in a directory.

Copy
ls | foreach {ren $_.fullname $_.name.replace("BI1", "BI2")}

Determining Interpreter Architecture

This example shows how to determine if the system is 32-bit or 64-bit.

Copy
if ([System.IntPtr]::Size -eq 4) { "32-bit" } else { "64-bit" }

Arrays

This example shows how to use arrays in PowerShell. The Process Definition has two Out Parameters, myArray and myArrayLength. The first will be populated with values as specified in the script, and the second will contain the number of elements in the array.

Copy
$myArray = @(10,"Hello",3.54321,"World")

foreach ($i in $myArray) {
 write-host $myArray[$i]
}

Note: Numbers are formatted according to your locale. With a German locale, for example, the array will be displayed as 10,Hello,3,54321,World, because the decimal point is a , in German locales. In this example, the myArrayLength Parameter is correctly filled with 4.

This example shows how to populate an array Parameter from PowerShell. Note the comma used to indicate to append to existing array.

Copy
$MyOutArray = $MyOutArray
$myArray = @(10,"Hello",3.54321,"World")
foreach ($i in $myArray) {
  $MyOutArray = $MyOutArray + ,$myArray[$i]
}

Taking a Screenshot of an Application

The following example requires a Run As User field prefix of {session}, followed by a user/credential. A session must be open on the target host (where the Platform Agent runs) for that user.

Copy
$app = Start-Process notepad -ArgumentList /redwood/Books.rtx -passthru
#Sleep some to ensure notepad has time to open
start-sleep -m 500
jtool screenshot -p $app.Id
$app.CloseMainWindow()
$app.Close()