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
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:
- Uses latest installed version of PowerShell: by default, the Run .Net Script activity runs scripts with PowerShell 2.0, which lacks many features of newer versions. This method will use the latest version installed on the runbook server.
- Uses 64-bit process: by default, the Run .Net Script activity runs scripts in a 32-bit process, because Orchestrator is an x86 application. This technique ensures scripts run in native 64-bit PowerShell, which avoids some potential compatibility problems.
- Captures detailed logs: keeps a trace log for easier troubleshooting
- Allows custom published data: provides any script variable values back to the runbook for continued use
- Robust error handling: all errors are caught and handled by the script, with error messages published to the data bus
- Semantically-correct result status: allows defining your own success criteria so you can take correct action when something didn’t work as intended, even if the script ran without throwing any exceptions
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.
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.
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.
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.
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.
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.
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:
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):
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.
Note: open the editor as administrator to ensure Invoke-Command will be allowed to run. Otherwise, you might receive this error:
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:
To fix this, unblock the file from its properties dialog:
As always, let us know if you run into problems using this example, and we’ll help sort it out.