Calling Orchestrator Runbooks (& retrieving output) via REST

This example uses PowerShell to call an Orchestrator runbook and display the results.

To demonstrate the scripting, I have created a simple runbook that will accept a string input.  It’s titled “Input1”.

The runbook isn’t impressive or complex.  The workflow will take the input variable… add some text to it… and store it as a variable.  That variable will be then returned by a completed runbook.

Orchestrator Runbook Process

To action a runbook with PowerShell a series of exchanges need to occur between the script and Orchestrator Web Service.  This is primarily because all items in a runbook are objects that are called by object GUIDs, not friendly names.  To initiate the runbook and then retrieve the output, a series of calls need to be made to the web service to identify the GUIDs.

Executing a runbook and retrieving the results can be thought of as 5 separate exchanges.

Retrieve the Runbook GUID

http://Orchestrator:81/Orchestrator2012/Orchestrator.svc/Runbooks?`$filter=Name eq 'My Runbook'"

A simple web request with filter for the name of a runbook returns an XML feed.  Within the feed is the GUID assigned to the particular runbook.

Retrieve Property GUIDs

http://smaserver:81/Orchestrator2012/Orchestrator.svc/Runbooks(guid'ae414022-740f-4118-b66c-fbd10682a2bb')/Parameters

After retrieving the Runbook GUID, a query can then be made on the parameters of that runbook.  The XML returned will contain the GUIDs (Id’s) used by Orchestrator for assigning data to input parameters of the runbook being called.  The PowerShell script will need to retrieve these identifiers to be able to submit a request to the Orchestrator Service

POST the Orchestrator Request

The body of the request being posted to the Orchestrator server.  The request is made as part of a standard XML template that includes the runbook GUID and GUIDs for any additional input parameters that the runbook requires.

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<entry xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" 
xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" 
xmlns="http://www.w3.org/2005/Atom">
<content type="application/xml">
<m:properties>
<d:RunbookId type="Edm.Guid">{$($RunbookID)}</d:RunbookId>
<d:Parameters>
  <Data>
    <Parameter>
      <ID>{$($RetreivedGUID)}</ID>
      <Value>$($RunbookInputValue)</Value>
    </Parameter>
   </Data>
 </d:Parameters>
</m:properties>
</content>
</entry>

The template that PowerShell will use is the same as the representation above but bracket are replaced with HTML Special entities.

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<entry xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" xmlns="http://www.w3.org/2005/Atom">
<content type="application/xml">
<m:properties>
<d:RunbookId type="Edm.Guid">{$($RunbookID)}</d:RunbookId>
<d:Parameters>&lt;Data&gt;&lt;Parameter&gt;&lt;ID&gt;{$($RetreivedGUID)}&lt;/ID&gt;&lt;Value&gt;$($RunbookInputValue)&lt;/Value&gt;&lt;/Parameter&gt;&lt;/Data&gt;</d:Parameters>
</m:properties>
</content>
</entry> 

The submitted POST request will return a Job GUID that can be used to monitor the status of the job and eventually retrieve the output from the runbook.

http://smaserver:81/Orchestrator2012/Orchestrator.svc/Jobs(guid'9c941f25-0097-4667-9bff-e8b3ee95e5f6')

After a runbook has completed the statu returned from the Job Query will change from “Pending” to “Completed”

Retrieve the Instance

With a completed Runbook job, it is possible to view the Instances of the job that has run.  This occurs through appending “/Instances” to the URL line.

http://smaserver:81/Orchestrator2012/Orchestrator.svc/Jobs(guid'9c941f25-0097-4667-9bff-e8b3ee95e5f6')/Instances

This returns a new instances URL

Retrieve Property Values

The Instances URL is appended with a “/Parameters” string for the values returned from the runbook to be displated.

http://smaserver:81/Orchestrator2012/Orchestrator.svc/RunbookInstances(guid'98c5aec1-119c-417c-b94a-b5cad3b8da32')/Parameters

The returned XML data contains the response from the executer runbook.

Powershell Code

The code below is an example of calling a runbook and retrieving the resultant data from the executed workflow.

##########################
# Powershell Example of calling an Orchestrator Runbook
# and retrieving the runbook output 
 
#Credentials
$secpasswd = ConvertTo-SecureString "MyPassword" -AsPlainText -Force
$mycreds   = New-Object System.Management.Automation.PSCredential ("domain\myusername", $secpasswd)
 
#Orchestrator Server Hostname
$OrchServer = "smaserver"
 
# Runbook to be called with an example Property and Value
$RunBookName 3. = "RunBookExample"
$RunbookInputProperty = "Input1"
$RunbookInputValue    = "PC12345"
 
#Begin ################
 
cls
# First Grab the GUID of the desired runbook
 
$OrchURI        = "http://$($OrchServer):81/Orchestrator2012/Orchestrator.svc/Runbooks?`$filter=Name eq '$RunBookName'" 
$ResponseObject = invoke-webrequest -Uri $OrchURI -method Get -Credential $mycreds 
$XML            = [xml] $ResponseObject.Content
$RunbookGUIDURL = $XML.feed.entry.id
 
write-host "Runbook GUID URI = " $RunbookGUIDURL
 
# User the runbook GUID to retrieve GUID values for Input Properties
# This example will retrieve an input property titled "Input1" as set in the variables
# in the header of this script
 
$ResponseObject = invoke-webrequest -Uri "$($RunbookGUIDURL)/Parameters" -method Get -Credential $mycreds 
[System.Xml.XmlDocument] $XML = $ResponseObject.Content
 
# A runbook contains a number of properties for the inputs and results returned from that runbook.
# All values within the returned XML need to be parsed to find which elements are Input properties
# with the desired name.  Once found, the "Id" or GUID for that particular property must be returned.
 
function GetScorchProperty([System.Object]$XMLString, [string]$Name, [string]$Direction, [string]$DesiredData){
 
   $nsmgr = New-Object System.XML.XmlNamespaceManager($XMLString.NameTable)    
   $nsmgr.AddNamespace('d','http://schemas.microsoft.com/ado/2007/08/dataservices')
   $nsmgr.AddNamespace('m','http://schemas.microsoft.com/ado/2007/08/dataservices/metadata')
 
 
   # Create an Array of Properties based on the 'Name' value
 
    $inputs = $XMLString.SelectNodes('//d:Name',$nsmgr)
 
   foreach ($parameter in $inputs){
      # Each 'Name' has related elements at the same level in XML
      # So the parent node is found and a new array of siblings 
      # is created.
 
      #Reset Property values 
      $obName          =""
      $obId            =""
      $obType          =""
      $obDirection     =""
      $obDescription   =""
 
      $siblings = $($parameter.ParentNode.ChildNodes)
 
      # Each of the sibling properties is identified
      foreach ($elements in $siblings){
      # write-host "Element = " $elements.ToString()
          If ($elements.ToString() -eq "Name"){
            $obName = $elements.InnerText
          }   
          If ($elements.ToString() -eq "Id"){
             $obId = $elements.InnerText
          }
          If ($elements.ToString() -eq "type"){
             $obType = $elements.InnerText
          }
          If ($elements.ToString() -eq "Direction"){
             $obDirection = $elements.InnerText
          }
         If ($elements.ToString() -eq "Description"){
            $obDescription = $elements.InnerText
         }
         If ($elements.ToString() -eq "Value"){
           # write-host "Value = "$elements.InnerText
            $obValue = $elements.InnerText
         }
       }
 
        if (($Name -eq $obName) -and ($Direction -eq $obDirection)){
          # "Correct input found"
          #Return the Requested Property
 
         If ($DesiredData -eq "Id"){
            return $obId 
         }
         If ($DesiredData -eq "Value"){
            return $obValue
         }
          }
   }
   return $Null
}
 
#The Function is called to retreive the "Id" Property
# This occurs by:
# + Passing in an XML object
# + Specifying the name of the propery being searched for (Input1)
# + Specifying that the runbook property is an "In" direction property
# + Specifying that the element neded for that property is the GUID based Id
 
$RetreivedGUID = GetScorchProperty $XML $RunbookInputProperty "In" "Id"
write-host "Property GUID = " $RetreivedGUID
 
# Derive the Runbook GUID
$urlstring = $RunbookGUIDURL
#    eg.     "http://server2012:81/Orchestrator2012/Orchestrator.svc/Runbooks(guid'c88ca155-e067-4f37-9723-ef977ac74047')"
 
$RunbookID = $RunbookGUIDURL.Substring($RunbookGUIDURL.Length - 38,36)
write-host "RunbookID = " $RunbookID 
 
# Submitting an Orchestrator Request requires a POST to call a runbook based on its GUID Id value
# Values for required inputs are submitted alongside Property GUIDs
# The XML structure of the data section will resemble:
#
# <Data>
# <Parameter>
#   <ID></ID>
#   <Value></Value>
# </Parameter>
# <Parameter>
#   <ID></ID>
#   <Value></Value>
# </Parameter>
# </Data>
#
# The XML structure uses HTML special entities to represent greater than & less than chracters.
 
$POSTBody = @"
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<entry xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" xmlns="http://www.w3.org/2005/Atom">
<content type="application/xml">
<m:properties>
<d:RunbookId type="Edm.Guid">{$($RunbookID)}</d:RunbookId>
<d:Parameters>&lt;Data&gt;&lt;Parameter&gt;&lt;ID&gt;{$($RetreivedGUID)}&lt;/ID&gt;&lt;Value&gt;$($RunbookInputValue)&lt;/Value&gt;&lt;/Parameter&gt;&lt;/Data&gt;</d:Parameters>
</m:properties>
</content>
</entry>
"@
 
 
# Submit Orchestrator Request
$OrchURI = "http://$($OrchServer):81/Orchestrator2012/Orchestrator.svc/Jobs/"
write-host "POST request URI " $OrchURI 
 
$ResponseObject = invoke-webrequest -Uri $OrchURI -method POST -Credential $mycreds -Body $POSTBody -ContentType "application/atom+xml" 
 
#Retrieve the Job ID from the submitted request
$XML               = [xml] $ResponseObject.Content
$RunbookJobURL     = $XML.entry.id
 
write-host "Runbook Job URI " $RunbookJobURL
 
# Runbooks will take some time to complete
# This example ues a simple loop to check if the job is still running
# a production script would use more error handling to ensure that the runbook hadn't failed
 
$status = $xml.entry.content.properties.Status
write-host "Current Status = " $status
 
do
{
	if($status -eq "Pending")
	{
		start-sleep -second 5
		$SleepCounter = $SleepCounter + 1
			if($SleepCounter -eq 20)
			{
				$DoExit="Yes"
			}
	}
	Else
	{
		$DoExit="Yes"
	}
 
    # Query the web service for the current status
     $ResponseObject = invoke-webrequest -Uri "$($RunbookJobURL)" -method Get -Credential $mycreds 
     $XML      = [xml] $ResponseObject.Content
     $RunbookJobURL = $XML.entry.id
     $status = $xml.entry.content.properties.Status
     write-host "Current Status = " $status
}While($DoExit -ne "Yes")
 
 
# As the runbook is no longer active, query the Instance of the submitted job
$ResponseObject = invoke-webrequest -Uri "$($RunbookJobURL)/Instances" -method Get -Credential $mycreds 
 
#Retrieve the Instance ID
$XML                = [xml] $ResponseObject
$RunbookInstanceURL = $XML.feed.entry.id
write-host "Runbook Instance URI " $RunbookInstanceURL
 
#The Instance can be used to retrieve the Parameters for the particular job
$ResponseObject                 = invoke-webrequest -Uri "$($RunbookInstanceURL)/Parameters" -method Get -Credential $mycreds 
[System.Xml.XmlDocument] $xml   = $ResponseObject.Content
 
#The parameters can be again parsed with the earlier function
# This occurs by:
# + Passing in an XML object
# + Specifying the name of the propery being searched for (Result)
# + Specifying that the runbook property is an "Out" direction property
# + Specifying that the element neded for that property is the "Value" of the property
 
$RunbookResult                   = GetScorchProperty $xml "Result" "Out" "Value"
 
write-host "Runbook Result " $RunbookResult