SCO: Run .NET Script Workarounds for PowerShell
By Gabriel Taylor
Published January 28, 2015
Estimated Reading Time: 4 minutes

Greetings and Felicitations, readers! This week’s blog post is on the subject of System Center Orchestrator and the very useful, yet sometimes fickle, Run .NET Script activity.

 

As many users are already aware, the Run .NET Script activity can be used to run PowerShell scripts directly within a runbook, processing or generating data and publishing it back out to the databus. This grants an extreme amount of flexibility to any workflow – in fact, because of the greater degree of power and flexibility afforded by PowerShell versus many of the Integration Pack activities, I often find myself creating runbooks that are primarily assembled with little more than linked PowerShell scripts, bypassing the Integration Pack activities altogether.

 

Still, the Run .NET Script activity is not without its limitations. One significant limitation is that the PowerShell session used by the Run .NET Script activity is stuck in PowerShell version 2.0, not supporting many of the current PowerShell capabilities and not able to load some PowerShell modules (such as that of SCVMM). As a result, scripts sometimes need to be written carefully in order to ensure they’ll function, or certain workarounds need to be put into place.

 

Here are two workarounds I use frequently when leveraging more advanced PowerShell scripts in Orchestrator:

 

Workaround #1

 

This first workaround involves opening a separate PowerShell session on the Runbook server, allowing use of the default version of PowerShell which is installed. This approach can be used to run specific commands, a series of commands, or an entire script in a separate session, and allows for data to be passed into the separate session and returned at the end. It functions by wrapping the desired script within a script block invoked simply by calling PowerShell:

## Pass a command or series of commands to a separate PowerShell session and collect the output
$Output = PowerShell {
    ## If needed, pass parameters into the separate session via a Param block.
    ## The input values should be appended to the script block, preceded by -Args, as seen on line 15
    param(
        $input1,
        $input2
    )

    ## Do Work Here

    ## Return the output by calling the variable or result here

} -args $Input1,$Input2

 

The sample above displays more complexity than is required, but illustrates how the technique can be used. Data can be passed into the separate session by creating a Param() block inside the script block, defining the number and type of inputs, and following the script block with the -Args parameter, followed by the values or variables to be passed in. Output can be captured by assigning the whole section to a variable and returning the desired data from within the script block. If no data needs to be passed back, all that is needed is to wrap the desired script in Powershell { ## Work } and call it a day.

 

This first workaround is the “quick and dirty” method – easy to implement, but limited in what it can do. It works fantastic, though, when the command or script can be ran locally and with the runbook service account’s credentials. When separate credentials or a separate server are needed, I leverage this second workaround.

 

Workaround #2

 

This second workaround is much more powerful, able to leverage a separate PowerShell session either local or remote, able to leverage separate credentials, and able to leverage variables from the host session without having to add a parameter block. This approach creates a new PSSession on the target computer and again leverages a script block to invoke commands within the separate section. This approach does require that PSRemoting is configured correctly and that the account being used has access to connect to the target computer, whether local or remote. Make certain to review the about_Remote_Requirements article on Technet to confirm the necessary server configuration is in place before usage, otherwise the about_Remote_Troubleshooting article will be your friend. Here’s a template for this technique:

## Define input parameters
$RemoteServer = "<ComputerNameHere>"

Try {
    ## Define Remote credentials and create a PSCredential object
    $RemoteUsername = '<UsernameVariableHere'
    $RemotePassword = ConvertTo-SecureString '<PasswordVariableHere>' -AsPlainText -Force
    $Creds = New-Object System.Management.Automation.PSCredential ($RemoteUsername, $RemotePassword) -ErrorAction:Stop
}
Catch {
    throw @"
Failed to create PS Credential object.
Exception: $($Error[0].Exception.Message)
"@
}

Try {
    ## Create a new session on the remote computer using the specified credentials
    ## NOTE - the Remote credentials MUST be granted permission to log in to the specified computer in order to complete this action!
    $RemoteSession = New-PSSession -Credential $Creds -ComputerName $RemoteServer -ErrorAction:Stop

    ## Do work in the remote session
    $Output = Invoke-Command -Session $RemoteSession -ScriptBlock {
        Try {
            ## Do Work Here; in order to use variables from the wrapper script, call them in the format "$Using:<VariableName>"

            ## If no error, store Success in a variable
            $Result = "Success"
            }
        Catch {
            ## Store the error in a variable
            $Result = $Error[0].Exception.Message
        }
        Finally {
            ## Return the result
            $Result
        }
    }

    ## Close the Remote session
    Remove-PSSession -Session $RemoteSession -ErrorAction:Stop
}
Catch {
    throw @"
Encountered a problem interacting with the remote session.
Exception: $($Error[0].Exception.Message)
"@
}
Finally {
    ## Check for success or failure
    If ($Output -ne "Success") {
        throw @"
Encountered a problem running the remote script.
Exception: $Output
"@
    }
}

 

As with the first sample, this displays more complexity than is absolutely needed, but illustrates how the technique can be used, including leveraging Try/Catch/Finally for error handling, both within the remote session and the host session. Additionally, a username and password can be passed into the script either from the databus or from a variable; these will be used to create a PSCredential object which will be used to open the separate session. If you’d like to call variables from the host session in the remote session, rather than requiring a Param() block to be used, the variables simply need to be prefaced with “$Using:” to inform PowerShell to pass them through. For example, to use a variable from the local scope called “OrganizationalUnit”, when referenced in the script block, it would need to be written as $Using:OrganizationalUnit and PowerShell would take care of the rest.

 

As part of the sample’s error handling, throw is used within the Catch{} script blocks to throw an exception in case of failure. In Orchestrator, this will have the result of causing the Run .NET Script activity to complete with an Error status and the error message from the script to be added to the activity’s Error Summary Text output. This can grant considerable insight into the cause of script failures via Orchestrator.

 

Closing Thoughts

 

Both of these workarounds are frequently used within my Orchestrator scripting. However, the techniques have a wide range of applications elsewhere as well – Workaround #1 is also very useful with custom workflows in Service Manager (like this one), and #2 is a staple for running PowerShell scripts against multiple servers. Hopefully these tricks can come in handy for you as well!

 

As always, please feel free to leave any questions or comments in the comment box below. Thanks for reading!

Article By Gabriel Taylor
With over 12 years of experience in the IT industry, Gabriel brings a focus on repeatable processes, solution design, and quality execution to Model’s Project Services practice. He believes the true value of technology is how it enables businesses to gain efficiencies, increase productivity, and achieve their goals. He is proud to work with Model’s team of experts to bring those benefits to Model’s clients.

Related Posts