Skip to content

Run PowerShell Script Activity

Nilesh Ghodekar edited this page Dec 25, 2015 · 29 revisions

Overview

The Run PowerShell Script activity allows you to run a PowerShell 2.0 script and optionally return the script output to the executing workflow for consumption in the subsequent activities.

Activity UI

Run PowerShell Script - Settings

Activity Display Name

Optional. Name of the activity to be displayed on the MIM / FIM workflow designer.

Advanced Features

Activity Execution Condition

Optional. The condition which must be satisfied for execution of this activity's core task (i.e. run the PowerShell script). This can be any WAL function expression resolving to a boolean value.

PowerShell Script User

Optional.The username of the user account to use while constructing a PowerShell Credential object to pass the script. The credential object is passed as a $Credential session variable to the script. The username must be in the Domain\UserName format if "Impersonate PowerShell User" setting is selected.

PowerShell Script User Password

Required when "PowerShell Script User" is specified. The password of the user account to use while constructing a PowerShell Credential objects to pass the script. The password value specified is an encrypted value using a certificate, DPAPI or RSA Machine Keys. Using a self-signed certificate is recommended due to ease of deployment. Please see EncryptedData.ps1 in the "SolutionOutput" folder that demonstrates how the password can be encrypted in the RunPowerShellScript Activity.

Impersonate PowerShell User

Optional. If this setting is selected, the PowerShell script is executed in the context of the specified RunAs user account. This option should be avoided, if the command-lets used in the script support a Credential parameter.

Load user profile

Optional. If this setting is selected, the user profile of the RunAs user is loaded during impersonation.

Log on type

Optional. Logon type to use during impersonation. For more information, refer to the dwLogonType documentation. The default value is "Log on as a batch job".

Script Location

Required. Specifies how the activity will locate the script for execution. The available options are:

  • Include in the Workflow Definition
  • Read from File
  • Resolve from Lookup
Script

Required when "Include in the Workflow Definition" option is selected for Script Location. In this case, the PowerShell script is stored as part of the workflow XOML.

Script Path

Required when "Read from File" option is selected for Script Location. In this case, the full path of the script located on a network share accessible by all FIMService instances is specified.

Script Lookup

Required when "Resolve from Lookup" option is selected for Script Location. In this case, a WAL Lookup expression which represents the script to be executed by the activity is specified.

Fail When Script is Missing

Required when "Resolve from Lookup" option is selected for Script Location. Specifies whether or not the activity should generate an error when the script lookup resolves to a null value.

Input Type

Optional. Specifies how the input should be provided to the PowerShell script. The available options are:

  • None
  • Named Parameters
  • Arguments
Named Parameters

Required when "Named Parameters" option is selected for script Input Type. When this option is selected, you can then define a list of parameter names expected by the script and corresponding WAL value expressions to be supplied to the script as the values of those parameters.

Arguments

Required when "Arguments" option is selected for script Input Type. When this option is selected, you can define a list of WAL value expressions to be passed as unnamed arguments to the script. The arguments are passed in the order they are listed.

Script Return Type

Optional. Specifies how the script output should be processed. The available options are:

  • None
  • Single Value
  • Table of Values

The activity can capture a return value from the PowerShell script and publish it to a lookup. Alternatively, the PowerShell script may return a Hashtable of key-value pairs which can be published to the workflow data dictionary WorkflowData using corresponding key names.

It should be noted that, in contrast to a "return statement" in a C# function, the return value from a PowerShell script / function is not necessarily what is returned by a return statement. Hence, output from any command or function that is not the intended return value from the script should be ignored (voided) or thrown away (Out-Nulled) so that it does not get piped to the output stream that is what is "returned" by the script.

Return Value Lookup

Required when "Single Value" option is selected for Script Return Type. When this option is selected, the script return value is published to the specified WAL Lookup expression.

Implementation Guidance

The RunPowerShellScript activity is a "catch_all" activity in WAL as anything can be scripted and executed when other native WAL activities fall short. But remember, with great power comes great responsibility :). Due to the obvious performance implications and the possibility of failures due to session throttling implemented by the remote systems such as Exchange, Lync/Skype as well as memory leaks that may be present in the PowerShell scripts and get accumulated over each run of the workflow, use of this activity should be avoided in production environments and particularly so in large scale, high-transaction implementations. Instead you should look at exploiting other native WAL activities or take a Management Agent based approach as much as possible. If you think you can avoid the use of RunPowerShellScript activity if a feature were added to WAL, please post the feedback on the WAL project forum.

The RunPowerShellScript activity has no way of knowing which errors in your script are terminating errors and which ones can simply be ignored. To be safe than sorry, the activity will treat any errors as fatal errors and abort the workflow. Hence when developing your script take a "pessimistic" approach to the script development and test for the success of a command before issuing the command in your script.

All MIM/FIM workflows run in a .NET Framework 3.5 runtime. This is a product limitation. This .NET runtime environment cannot execute scripts and command-lets that need PowerShell 3.0 or above runtime. If there is a need to execute a script containing PowerShell 3.0+ command-lets, they can be made to run in a separate process to avoid the product limitation. e.g. using PowerShell Remoting or launching a new "powershell.exe" session using Start-Process command-let. Examples of these two techniques are given below:

<#
This script uses PowerShell.exe to execute ActiveDirectory PowerShell 3.0 cmdlets
#>

param
(
	[parameter(mandatory = $true)] $AccountName
)

function InvokeImmediateTermination
{
    $stdOutFile = Join-Path $env:TEMP -ChildPath "StdOut_$AECRequestId.log"
    $stdErrFile = Join-Path $env:TEMP -ChildPath "StdErr_$AECRequestId.log"

    $command = @"
    & {
        if (!(Get-Module -Name "ActiveDirectory"))
        {
            Import-Module ActiveDirectory
        }

        Set-ADUser -Identity '$AccountName' -Enabled:`$false

        if (`$Error.Count -eq 0)
        {
            "!!Success!!"
        }
        else
        {
			# Can't easily use stdErrFile as it comes as CLIXML and includes Warning/Verbose/Debug streams as well. So we'll simply use stdOutFile
            "!!Error!!"
            `$Error
        }
    }
"@

    Write-Debug $command

    $commandBytes = [System.Text.Encoding]::Unicode.GetBytes($command)
    $encodedCommand = [Convert]::ToBase64String($commandBytes)

    Start-Process 'PowerShell.exe' `
        -ArgumentList "-Version 3.0 -NonInteractive -OutputFormat Text -EncodedCommand $encodedCommand" `
        -RedirectStandardOutput $stdOutFile `
        -RedirectStandardError $stdErrFile `
        -Wait

    $statusLine = Get-Content $stdOutFile| Select-Object -Last 2 | ? {
        $_.Contains("!!Success!!") 
    }
        
    if ([string]::IsNullOrEmpty($statusLine))
    {
        Write-Error ((Get-Content $stdOutFile) -join "`r`n") # TODO: Only get the lines after !!Error!! marker
    }
    else
    {
        Write-Debug "Script executed successfully."
    }

    Remove-Item $stdOutFile -Force
    Remove-Item $stdErrFile -Force
}

InvokeImmediateTermination
<#
This script uses Remote PowerShell to execute ActiveDirectory PowerShell 3.0 cmdlets
This script needs Remote PowerShell enabled on each FIMService server.
It also needs FIMService service account permissions to execute remote commands.
This can be configured using
	1. Enable-PSRemoting -Force
	2. Set-PSSessionConfiguration -Name Microsoft.PowerShell -ShowSecurityDescriptorUI
#>

param
(
	[parameter(mandatory = $true)] $AccountName
)

function InvokeImmediateTermination
{
    # Any errors during execution of the script or the script block are bubbled up automatically
    # Comment out -ComputerName parameter when running interactively
	Invoke-Command -ScriptBlock {
            param($AccountName)

            if (!(Get-Module -Name "ActiveDirectory"))
            {
                Import-Module ActiveDirectory
            }

            Set-ADUser -Identity $AccountName -Enabled:$false
        } -ArgumentList $AccountName -ComputerName localhost
}

InvokeImmediateTermination

The RunPowerShellScript activity passes the activity execution context as session variables AECWorkflowInstanceId, AECRequestId, AECActorId, AECTargetId and AECWorkflowDefinitionId). It also set ProgressPreference, DebugPreference and VerbosePreference as per trace levels in the app.config file of FIMService. It is recommended that you instrument your scripts using this information so that the workflow can be traced end-to-end. This is demonstrated in the sample script RunPSLoggingSample.ps1 that gets copied to the "SolutionOutput" folder.

Examples

Clone this wiki locally