PowerShell & System Center Orchestrator - Best Practice Template


A reusable template for “best practice” execution of PowerShell scripts within a System Center Orchestrator runbook using the built-in “Run .Net Script” activity.


Solution Guide

Click on "Run .Net Script - PowerShell" in the runbook viewer above to see the script

What it Does

This example shows a “best practice” method of executing PowerShell scripts within a System Center Orchestrator runbook using the built-in “Run .Net Script” activity. Using this method provides several benefits to overcome some inherent limitations of Orchestrator’s out-of-the-box PowerShell support:

Template Orchestrator PowerShell Script Activity

When to Use

You can use this example as a template any time you want to run PowerShell scripts in an Orchestrator runbook, unless you are sure you don’t need or want any of the features listed above.

How it Works

The Run .Net Script activity allows running PowerShell code directly by entering it into the activity’s “Script” field. This runs the script in a PowerShell process spawned by the Orchestrator runbook server. Unfortunately, this comes with some limitations as described above.

This example approach uses the same activity but with a particular PowerShell structure as a workaround for these issues. The pattern was originally described in the free eBook Microsoft System Center – Designing Orchestrator Runbooks, in the section “Using Windows PowerShell in Orchestrator” starting on page 61. I highly recommend reading at least this section, if not the rest of the book. The downloadable example script described here is a modified version of this approach.

The next sections discuss a few of the aspects of the script itself and this example runbook to show how it can be used.

Script Input

Any inputs to the script are provided at the top where they are defined as variables and assigned values from the data bus. These can be provided by subscribing to published data or to a global variable. They are then added as arguments passed to the script block executed subsequently by Invoke-Command.

PowerShell Script Data Bus Inputs
PowerShell Script Data Bus Inputs

Separate “External” Session

The key to the technique is doing all the “real work” of the script in a separate PowerShell session from the one initially created by Orchestrator. This lets us get into a fancy new 64-bit process with the latest PowerShell version where we can work with minimal risk of unexpected legacy behavior or issues. This is done by running the main script content as a script block using Invoke-Command.

Entering PowerShell session with latest 64-bit vesion
Entering PowerShell session with latest 64-bit version

 

Technically, this means we are setting up a “remote” session to the current machine, which means everything that happens in the script block is completely separate from the originating session. Then, we have to explicitly return whatever results we want Orchestrator to know about as we arrive back from this “remote” session.

Creating external PowerShell session from Orchestrator script activity
Creating external PowerShell session from Orchestrator script activity

 

Script Block: Work Done Here

Everything you want to accomplish, which is the reason why you’re running the PowerShell script to begin with, is defined in the script block passed to Invoke-Command. Specifically, you’ll put everything inside the try { } block so that any exceptions will be handled and provided back to the runbook.

Main script content goes inside try section within script block
Main script content goes inside try section within script block

 

Generally speaking, any PowerShell script content can be included here as you would a standalone script. A caveat to this is commands that connect and authenticate to external systems, which require dealing with the “double-hop” authentication scenario discussed under the implementation section below. This still works as long you take the steps to configure the runbook server appropriately.

Script Output / Published Data

The output that comes back from the “external” session is in the form of an array, which can include as many values as needed, including custom return values. These are then extracted from the array into separate variables to be provided by the script activity as published data. The template includes three basic return elements: ResultStatus, ErrorMessage, and TraceLog, which provide information about the outcome of the script execution.

Defining published data for PowerShell within Run .Net Script activity
Defining published data for PowerShell within Run .Net Script activity

 

Any additional custom return variables can be defined by simply adding them to the $resultArray variable that defines what is returned, then extracting them from the returned array.

Any variable extracted from $ReturnArray can then be published to the data bus by defining it within the Published Data section of the script activity properties.

Logging

Within the script block, record any useful action information into the trace log by using the AppendLog function and supplying a string to record. This adds an entry to the trace log that is produced.

If you want the script logs to be saved by Orchestrator, make sure to enable the option “Store activity-specific Published Data” for each runbook where you have a script activity that should record log data.

Logging values published by PowerShell script activity
Logging values published by PowerShell script activity

Error Handling

Any errors that occur in the script block are caught and recorded, then returned as published data (Error Message and Trace Log). You can then use the Result Status to determine whether the script succeeded, and to branch accordingly in your runbook. In the example, any exception will cause the Result Status to be “Failed”. The example runbook shows how to branch on this status in order to record the failure details in the event log and return a negative result for the overall runbook when script errors are encountered. For instance, the example script looks for an input value matching "bad stuff" which causes it to throw an exception. The resulting event log entry is shown below:

Recording detailed script error logs to Windows Event Log
Recording detailed script error logs to Windows Event Log

 

If there is an exception running the activity itself (before the script block runs), this should be reported in the normal Error Summary Text published data property.

Implementing In Your Environment

This example serves as a template for creating runbooks with PowerShell script activities. You can use the link at the top of the page to download the example runbook to get the template runbook activity and, for convenience, an associated PowerShell file with the script content. Once downloaded, use the Import function of Runbook Designer to bring the example into your Orchestrator environment for testing.

When creating a new script activity in your runbooks, you can copy/paste/modify either the whole template activity or just the template script content in a new Run .Net Script activity.

Prerequisites

This approach has been tested extensively with System Center Orchestrator 2012 SP1 and R2. I also recommend installing the latest official release of PowerShell on your runbook server(s).

Handling Remote Connections

Because the main script logic is performed in a secondary session, any connections from that logic to another system would constitute a second connection. So if the script needs to authenticate to a SharePoint server, for example, you’d be faced with a “multi-hop remoting” scenario. This is a confusing situation that requires a couple additional configuration actions to allow this to happen.

On Orchestrator Runbook Server(s), run each of these commands in an elevated PowerShell console (replacing “*.mydomain.com” with your domain name in the second command):

Enable-PSRemoting
Enable-WSManCredSSP -Role Client -DelegateComputer *.mydomain.com
Enable-WSManCredSSP -Role Server -Force

 

Development and Testing Tip

It can be helpful to develop scripts in the PowerShell ISE or another script editor before running in Orchestrator. You can do that with this template by replacing the initial data bus input variables with some test values while developing and testing the editor. Then, when the things are working, copy the script content into the script activity and replace the input values with the published data subscription references.

Orchestrator script development in ISE using mock test input values
Orchestrator script development in ISE using mock test input values

 

Note: open the editor as administrator to ensure Invoke-Command will be allowed to run. Otherwise, you might receive this error:

New-PSSession : [localhost] Connecting to remote server localhost failed with the following error message : Access is denied.

 

Also, you may receive a warning when trying to run the script after opening from the download, if the file is detected as coming from another computer:

File <path> cannot be loaded. The file <path> is not digitally signed. You cannot run this script on the current system.

 

To fix this, unblock the file from its properties dialog:

Unblock PowerShell file to allow execution in script editor
Unblock PowerShell file to allow execution in script editor

 

As always, let us know if you run into problems using this example, and we’ll help sort it out.

Discuss