Service Management Automation (SMA) is a capability in Windows Azure Pack and Orchestrator 2012 R2. Though SMA and Orchestrator are both built for IT process automation, orchestration, and integration, they are in many ways complimentary in their capabilities and use scenarios ( see this post for a discussion of SMA versus Orchestrator ). In general, SMA is optimal for use in Microsoft Cloud OS environments, while Orchestrator is best in traditional datacenter environments. IT groups that are moving some of their traditional workloads to the cloud may find it useful to leverage both SMA and Orchestrator; in this case, it may be common for some end-to-end processes to extend from SMA to Orchestrator and vice-versa.
In this post, I will discuss how SMA and Orchestrator can be integrated. In particular, I will focus on how SMA runbooks can call Orchestrator runbooks and how Orchestrator runbooks can call SMA runbooks, with input parameters and return data. Both scenarios use PowerShell modules that ship with the products as well as the OData web service endpoints that each product exposes.
Other useful posts on how to call Orchestrator from an SMA runbook by Tiander Turpijn should also be read: see Calling an Orchestrator Runbook from SMA Part 1 and Part 2 .
PowerShell Modules for Integration with SMA and Orchestrator
To simplify integration with SMA and Orchestrator we have created PowerShell modules with cmdlets that expose the key functionality available via the respective OData web services.
The next two images show the cmdlets available in each module.
Calling Orchestrator Runbooks from SMA
There are a few key steps you will need to take in any SMA runbook that calls an Orchestrator runbook; these steps are the following:
Let’s take a look at each section in more detail. For illustration, we will look at the PowerShell workflow code from an SMA runbook called Start-ScoRunbook. In the box below is the entire runbook script (also you can click here to DOWNLOAD the runbook and then import it to SMA).
001
002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030 031 032 033 034 035 036 037 038 039 040 041 042 043 044 045 046 047 048 049 050 051 052 053 054 055 056 057 058 059 060 061 062 063 064 065 066 067 068 069 070 071 072 073 074 075 076 077 078 079 080 081 082 083 084 085 086 087 088 089 090 091 092 093 094 095 096 097 098 099 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 |
########################################################################
# Copyright (c) Microsoft. All rights reserved. # This code is licensed under the Microsoft Public License. # THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF ANY KIND, EITHER # EXPRESS OR IMPLIED, INCLUDING ANY IMPLIED WARRANTIES OF FITNESS # FOR A PARTICULAR PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. ######################################################################## <# .SYNOPSIS This runbook starts an Orchestrator runbook with or without parameters and gets back any output. .DESCRIPTION This runbook illustrates how to start a runbook in System Center Orchestrator, pass in parameters, and get back return data. If the Orchestrator runbook has output, then this runbook will return it as a hashtable with key/value pairs, where the key is the output parameter name .PARAMETER RunbookPath String. The full path to the runbook as defined in Orchestrator. For example, \folder1\folder2\runbookname. .PARAMETER InputParams Object. Input parameters formatted as PSCustomObject with key/value pairs. .PARAMETER JobCheckIntervalInSeconds Int. The amount of time, in seconds, to sleep between attempts to check the job for completeness. Set this to greater than the expected run time of the job. .NOTES The runbook expects to connect with the web service for an installation of Orchestrator 2012 or 2012 R2. The runbook assumes that you have created an Automation Connection asset named "OrchestratorConnection" with the information required to connect with the Orchestrator web service. #> workflow Start-ScoRunbook { [OutputType( [hashtable] )] # define the input parameters to this runbook param ( [Parameter(Mandatory=$true)] [string] $RunbookPath, [Parameter(Mandatory=$false)] [object] $InputParams, [Parameter(Mandatory=$false)] [int] $JobCheckIntervalInSeconds ) # get the Orchestrator connection asset $con = Get-AutomationConnection -Name 'OrchestratorConnection' # create a Credential object $securepassword = ConvertTo-SecureString -AsPlainText -String $con.UserPassword -Force $domainuser = $con.UserDomain + "\" + $con.UserName $creds = New-Object -TypeName System.Net.NetworkCredential -ArgumentList ($domainuser, $securepassword) # create the URL for the Orchestrator service $url = Get-OrchestratorServiceUrl -Server $con.ServerName # get the SCO runbook we want to start $runbook = Get-OrchestratorRunbook -ServiceUrl $url -Credentials $creds -RunbookPath $RunbookPath if ($runbook -eq $null) { $msg = "Orchestrator runbook '$RunbookPath' could not be retrieved." Write-Error -message $msg Throw $msg } else { # start the runbook job if ($InputParams -ne $null) { # convert the input param names to their associated GUIDs $RBInputWithIds = getParamObjectWithIds -RBInputWithNames $InputParams -runbook $runbook # start the runbook with input parameters and get the job returned $job = Start-OrchestratorRunbook -Runbook $runbook -Credentials $creds -Parameters $RBInputWithIds } else { # start the runbook without any input parameters and get the job returned $job = Start-OrchestratorRunbook -Runbook $runbook -Credentials $creds } # if a job has been created then get any output if ($job -eq $null) { $msg = "Orchestrator runbook job is null: no job was created." Write-Error -message $msg Throw $msg } else { # get any output $out = getJobOutput -Job $job -Creds $creds -JobCheckIntervalInSeconds $JobCheckIntervalInSeconds # return the output object if there is output if ($out -ne $null) { Write-Output $out } } } # # Function that takes an input parameter object that has parameter names and values # and replaces the names with the ids # function getParamObjectWithIds { param ( [object] $RBInputWithNames, [object] $runbook ) # convert the PSCustomObject to a hashtable $NamesHt = @{} $RBInputWithNames.psobject.properties | Foreach { $NamesHt[$_.Name] = $_.Value } # get the runbook parameters $RBParams = $runbook.Parameters # create new input parameter hashtable with parameter ids as keys $RBInputWithIds = @{} foreach($key in @($NamesHt.keys)) { foreach ($pm in $RBParams) { if ($pm.Name -eq $key) { $RBInputWithIds.Add($pm.Id,$NamesHt[$key]) } } } # output the new parameter hashtable Write-Output $RBInputWithIds } # # Function that gets the runbook job output # function getJobOutput { param ( [object] $Job, [object] $Creds, [int] $JobCheckIntervalInSeconds ) # assure the job is complete while( ($job.Status -eq "Running") -or ($job.Status -eq "Pending") ) { Start-Sleep -s $JobCheckIntervalInSeconds $job = Get-OrchestratorJob -jobid $job.Id -serviceurl $job.Url_Service -credentials $creds } # get the runbook instance that has the job data $instance = Get-OrchestratorRunbookInstance -Job $job -Credentials $creds if ($instance -eq $null) { $msg = "Orchestrator runbook instance is null." Write-Error -message $msg Throw $msg } else { # there are instance parameters only if the runbook has input and/or output parameters $instparams = Get-OrchestratorRunbookInstanceParameter -RunbookInstance $instance -Credentials $creds if ($instparams -ne $null) { # any output will be in a hashtable $out = @{} # look through the runbook parameters for any that are for output foreach ($instparam in $instparams) { if ($instparam.Direction -eq "Out") { # write the output value (always a string, interger, date, or boolean) $out.Add($instparam.Name,$instparam.Value) } } Write-Output $out } else { Write-Verbose -message "The runbook has no output." -Verbose Write-Output $null } } } } |
Create a connection between SMA and Orchestrator and get an Orchestrator runbook
The first section of code above shows how to create the connection from SMA to Orchestrator and get the Orchestrator runbook. Before authoring the runbook I created an SMA Connection setting with the parameters for connecting to my Orchestrator web service ( read this to learn more about the Orchestrator web service ).
The Get-AutomationConnection activity that is installed with SMA is used to retrieve the connection object. From that connection object we extract the UserDomain, UserName, and UserPassword fields that we use to create the credential object that will be used for authorization with Orchestrator. We also extract the ServerName that the Get-OrchestratorServiceUrl cmdlet used to create the full URL to the Orchestrator web service. With the URL, the credentials, and the runbook path we then get the Orchestrator runbook object using the Get-OrchestratorRunbook cmdlet.
Start the Orchestrator runbook
Now that we have the runbook object and any input parameters we are ready to start the runbook. To start a runbook we use the Start-OrchestratorRunbook cmdlet, which takes the runbook object, the credentials, and any input parameters. If there are input parameters to the runbook, we first must get the GUIDs for each named parameter and create a hashtable of the GUIDs and values (the getParamObjectWithIds function does this).
Monitor the Orchestrator job for completion and get any output
If Start-OrchestratorRunbook is successful, then it returns the Orchestrator job object. The next step is to monitor the job for completion and then get any output: the getJobOutput function performs this work.
To monitor a job and determine if it has completed we use the Get-OrchestratorJob cmdlet and wait until the job status is not Running or Pending. Note that the sleep interval is configurable: you will want to make this number greater than the expected job run time. If the job is not one of these states, then it has reached a final state and we can attempt to get the runbook instance associated with the job, from which we can extract any runbook instance parameter that has the Direction metadata equal to Out (which contain the runbook output). If there are output parameters then we package them in a hashtable and return it.
Calling SMA Runbooks from Orchestrator
The key logic in an Orchestrator runbook that calls an SMA runbook is quite similar to that discussed above.
The main difference between this runbook and that discussed in the previous section is that this Orchestrator runbook will contain all of the core logic in a PowerShell script that uses the SMA PowerShell module. This script will be contained in an Orchestrator Run .Net Script activity ( click here to DOWNLOAD the script ). Because the SMA PowerShell module requires a 64-bit PowerShell process to run, and Orchestrator is constrained to a 32-bit process, the script uses PowerShell remoting to run the SMA cmdlets on the 64-bit Windows server where the ServiceManagementAutomation module is installed. Remember to run “Set-ExecutionPolicy RemoteSigned” in the PowerShell console on the remote machine so that the remoting is allowed.
This next screenshot shows a simple Orchestrator runbook that takes in input parameters, uses the Run .Net Script activity to run the script that calls SMA, and then returns any output. In general, your runbooks will be more complex than this, however the key logic for integrating with SMA will be contained in the single Run .Net Script activity.
For the connection and credential values in the script you will probably want to use variables stored in Orchestrator Global Settings so that this connection information can be re-used in many runbooks.
The PowerShell script for starting an SMA runbook, monitoring the job, and getting return data
In the box below you can see the entire script. This script was first authored and tested in the PowerShell ISE and then pasted into the Run .Net Script activity.
001
002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030 031 032 033 034 035 036 037 038 039 040 041 042 043 044 045 046 047 048 049 050 051 052 |
$ErrorActionPreference = "Stop"
try { # credentials $user = "domain\username" $pwd = ConvertTo-SecureString –String "password" –AsPlainText -Force $cred = New-Object –TypeName System.Management.Automation.PSCredential –ArgumentList $user, $pwd # web service endpoint $computername = "sma-server" $wsep = "https://$computername" $port = "9090" # create persistent connection to remote computer $session = New-PSSession -ComputerName $computername -Credential $cred # run the script block in the remote session and get back the output $out = Invoke-Command -Session $session -ArgumentList $cred,$wsep,$port -scriptblock { $cred = $args[0] $wsep = $args[1] $port = $args[2] # set the runbook name and any input parameters $RBName = "SMARunbook" $params = @{"Param1"="StringValue";"Param2"=123} # start the SMA runbook $thejobid = Start-SmaRunbook -Name $RBName -Parameters $params -WebServiceEndpoint $wsep -Port $port -AuthenticationType Basic -Credential $cred # monitor the SMA job until completed $shouldLoop = $true while($shouldLoop) { # the sleep interval, in seconds, should be greater than the expected run time of the SMA job $sleepinterval = 5 Start-Sleep -s $sleepinterval $job = Get-SmaJob -Id $thejobid -WebServiceEndpoint $wsep -Port $port -AuthenticationType Basic -Credential $cred $status = $job.JobStatus $shouldLoop = (($status -ne "Completed") -and ($status -ne "Failed") -and ($status -ne "Suspended") -and ($status -ne "Stopped")) } # get the job output $jobout = Get-SmaJobOutput -Id $thejobid -Stream Output -WebServiceEndpoint $wsep -Port $port -AuthenticationType Basic -Credential $cred $jobout.StreamText } $out } catch { Throw $_.Exception } |
In the first part of the script the credential and connection objects are created. Then a connection session is created to the remote server.
The key part of the script occurs next when we use the Invoke-Command cmdlet to execute a script block in a PowerShell session on the remote computer. Parameters are passed into the script block using the –ArgumentList parameter.
Within the script block the SMA runbook is started using the Start-SmaRunbook cmdlet, and parameters are passed to it in a hashtable object.
A job id is returned from the Start-SmaRunbook cmdlet, and this job id is used to monitor the job using the Get-SmaJob cmdlet. When the job status has reached a final state, then we use the StreamText property in the objects returned from Get-SmaJobOutput to get any output. Note that in this example we don’t check that the final state is actually “Completed”, so you will want to add logic to handle cases where the runbook is in some other state.
Also note that the Get-SmaJob cmdlet in a single collection and does not guarantee that the order of records returned is chronological. If you want them in chronological order then PowerShell script like this can help:
Get-SmaJobOutput <params here> | %{ $_ } | sort StreamTIme
To get the output from the script into the Orchestrator databus where it can be used by other activities in the Orchestrator runbook, we configure the Run .Net Script activity Published Data to put the contents of the script “out” variable into the databus property named “Return from SMA”.
Summary
As you can see, once you understand the mechanics of connecting between SMA and Orchestrator, starting runbooks, monitoring jobs, and getting output data, the goal of creating end-to-end processes is relatively straightforward. Go ahead and write a simple runbook in SMA that calls Orchestrator, and vice-versa; from those experiments you should be able to get a good baseline of understanding from which you can build more complex scenarios.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.