Tuesday, June 19, 2012

FTP Integration Pack Update v1.2

****Update 20120918****
I've just released v1.3 of the FTP/SFTP IP.
Post:
http://jmattivi.blogspot.com/2012/09/scorch-ftp-integration-pack-update-v13.html
CodePlex:
https://scorchestrator.codeplex.com/releases/view/94807


 ****Update****
I've updated the IP to v1.2 to fix an issue w/ the List Folder activity when using Secure = False.  Updated build has been posted to codeplex.
https://scorchestrator.codeplex.com/releases/view/89913


I've kicked the tires on the FTP Integration Pack a bit more and have updated the List Folder activity.  It will now only output the directory contents rather than displaying the entire verbose output (which is kinda redundant from the results published data).

I've also updated the download to Stable from Beta.  Please let me know if any bugs are encountered and/or if there are any feature requests.

Unfortunately, the FTPWebRequest class doesn't support wildcards for Secure = False (FTP). However, the Secure = True method (SFTP) in the IP does support using wildcards such as * or *.txt.

One way to get around the FTP method using wildcards would be to use the List Folder activity to find the files in the folder, parse them, and then pass each into the Download File activity.

For instance, use List Folder to get the files in the Output published data:
07-12-12 08:56AM 0 asdf.txt
07-12-12 08:56AM 0 blah.txt

Parse the Output and pass asdf.txt and blah.txt to the Download File activity.


The Integration Pack can be downloaded from this link below:
https://scorchestrator.codeplex.com/releases/view/89913



Tuesday, June 12, 2012

Integration Pack for FTP

****Update 20120918****
I've just released v1.3 of the FTP/SFTP IP.
Post:

http://jmattivi.blogspot.com/2012/09/scorch-ftp-integration-pack-update-v13.html
CodePlex:

https://scorchestrator.codeplex.com/releases/view/94807

********Please see this post for the updated v1.2
http://jmattivi.blogspot.com/2012/06/ftp-integration-pack-update-v11.html
********

I've just published an FTP/SFTP Integration Pack that can be downloaded from https://scorchestrator.codeplex.com/releases/view/89427.

Here are the current activities supported in the IP.  The SFTP portion is based off of Putty's psftp software (documentation can be referenced here).  The FTP functionality is based off of the System.Net.FtpWebRequest Class (documentation can be referenced here).  I was looking to provide roughly the same functionality the FTP Integration Pack for Opalis had.



All fields are required for each activity.   Secure and AutoAcceptKey are set to True by default

Secure = True
 --SFTP

 Secure = False
 --FTP

AutoAcceptKey only applies if Secure = True
AutoAcceptKey  = True
--Automatically accept the host key presented by the server
****This can be insecure****

AutoAcceptKey = False
--This will technically cause the application to prompt to accept the key, which in turn would cause the activity to fail.  However, you can proactively manually store the host key and leave this set to false if security requires it.


Unfortunately, the FTPWebRequest class doesn't support wildcards for Secure = False (FTP). However, the Secure = True method (SFTP) in the IP does support using wildcards such as * or *.txt.

One way to get around the FTP method using wildcards would be to use the List Folder activity to find the files in the folder, parse them, and then pass each into the Download File activity.

For instance, use List Folder to get the files in the Output published data:
07-12-12 08:56AM 0 asdf.txt
07-12-12 08:56AM 0 blah.txt

Parse the Output and pass asdf.txt and blah.txt to the Download File activity.
 

Upload File Example:




Download File Example:



Please kick the tires on this and let me know if you find any bugs or see the potential for functionality improvements.

This is the first time I've really poked around w/ the OIT command line activity wizard....now I have to teach myself C# here shortly to start building projects in Visual Studio w/ the SDK going forward. :)

Wednesday, June 6, 2012

Dynamic Sleep Activity to Schedule Runbook

I was designing a workflow where there was a Main Runbook that called RunbookA and Runbook B. 

The catch was RunbookA was invoked at 3pm and looped for 3 hours until a file was processed successfully (so there was no standard end time).  Also, RunbookB needed to run at 6pm only if RunbookA was successful.

So I needed a way to determine/schedule RunbookB to run at 6pm if RunbookA completed at 3pm or 4pm or 5pm (you get the point).  So I used PowerShell to create a dynamic Sleep based on when RunbookA completed to invoke RunbookB at 6pm.


Saturday, June 2, 2012

High Availability for Runbook Servers on Invoking Runbooks

Here's just a quick tidbit for specifying runbook servers used in the Invoke Runbook standard activity.  It’s beneficial to keep the runbook server names stored in Global Variables.  This way you can insert the variable in the Invoke Runbook activity instead of hardcoding the server name.

In case a runbook server has issues and is powered down or unavailable, the invoke runbook activity will automatically start the invoked runbook on the next runbook server in line.  Another benefit is the ability to add/remove runbook servers and just update a variable than trying to find/replace every invoke runbook activity w/ the new server name.

You can also use the variables to group runbook servers based on their role in the environment.  So to load balance runbooks, depending on how many runbook servers you have (for this example we’ll say three internal).  You could specify three variables which have the following values.

Primary:  myrunbookserver1;myrunbookserver2;myrunbookserver3
Secondary:  myrunbookserver2;myrunbookserver3;myrunbookserver1
Tertiary:  myrunbookserver3;myrunbookserver1;myrunbookserver2

So for runbook servers interacting w/ servers on an internal domain you could use:



So for runbook servers interacting w/ servers on a dmz domain you could use:



Finally, subscribe to the variable in the Invoke Runbook activity.

Implementing SSL on the OC and Web Service

This post I'll explain how to setup SSL on the Orchestration Console (OC) and Web Service (WS) while also redirecting http traffic to https.  For these examples, I’ve setup the OC on port 443 and the WS on 8443.

Since the OC and WS are now hosted through IIS, all the settings will be set through IIS.  First off you need to setup the bindings on the sites for each like below.  I'll explain in a bit why we want to leave 80 enabled in the bindings.

Orchestration Console Bindings


Within the https port from Edit, you can upload and select the cert you wish to use on the server from the drop down list.


Web Service Bindings


Now that the OC and WS are enabled for https, we need to setup redirection from port 80 over to port 443 on the Orchestration Console.  Based on what was previously done, we only need to do this for the OC since the WS is only enabled for port 8443.  For a great resource on the redirection, see this link that explains how to do this using the URL Rewrite tool – http://www.jppinto.com/2010/03/automatically-redirect-http-requests-to-https-on-iis7-using-url-rewrite-2-0/.

To have the redirection work successfully using the URL Rewrite tool, it’s necessary that the SSL settings are left at the default NOT to require SSL on the OC.  This allows the traffic to hit port 80 and then the redirection will kick in.  This is why port 80 is left on the bindings as well.



Finally, we need to edit the OC’s web.config file with the new "https://" address and fqdn path of the WS.  You can also see at the bottom of the screenshot below where the URL Rewrite tool adds the redirection.



If you don't edit the OC’s web.config file, you'll get this error after opening/logging into the OC since it can’t find the web service.



Now when you browse to the Orchestration Console on port 80 using the server's host name, IIS will automatically redirect the connection over to https.

By default, the Orchestration Console and Web Service also have pass through authentication enabled.  If you use privileged accounts to perform administrative tasks besides the account you regularly login to Windows with, you’ll also want to setup Basic Authentication on both the OC and WS.  This way when you browse to the site, it will prompt for a username and password to login with.

Prevent Multiple Invocations of a Runbook

There are many occasions where you may want to prevent someone (or schedule) from starting (queuing up) another instance of a Runbook if it's already running.  There are three ways I've found to accomplish this….using counters, the database query activity to look up a running job, and the web service.

While using counters may be the best solution to prevent workflows from running before a prerequisite workflow completes, it's not the best solution to prevent inadvertently queuing up another runbook instance.

The database query activity can be used in this scenario to query the PolicyInstances table to see if a specific runbook is already running.





The query used would look like this.  Note you need to use the specific Runbook name in the query.

Select POLICIES.Name,POLICYINSTANCES.Status,POLICYINSTANCES.TimeStarted,POLICYINSTANCES.TimeEnded
From POLICYINSTANCES,POLICIES
Where (POLICIES.UniqueID = POLICYINSTANCES.PolicyID) AND TimeEnded is null AND POLICIES.Name = '0.1 Runbook B'

Based on the query results, you can use the Link logic to either start the Runbook or send an email that the Runbook is already running and cannot start at this time.



The .NET Script activity can be used in this scenario to query the Jobs collection of the Web Service to see if a specific runbook is already running.



 The Powershell script to query the Jobs collection is a little different than I've used in prior posts.  Sometimes it's tedious to look up the RunbookID guid, so this way you can specify the name of the runbook without the guid.  This is accomplished by pointing to the Jobs collection, but also expanding the runbook collection as well to grab it's associated data.  Then you can use the filter and select statements in the odata query to filter on the name of the runbook.

Here is the URL for the GET request.  Note you need to specify the collection name and property name together for the collection that's expanded.

$url = "http://scorch.domain:81/Orchestrator2012/Orchestrator.svc/Jobs()?`$expand=Runbook&`$filter=(Status eq 'Running') and (Runbook/Name eq '0.1 Runbook B')&`$select=Runbook/Name,Status"

Here is the complete script to query the web service (highlighted fields would need to be updated per your environment):

#########################################################################################

$user = "domain\username"
$pass = ConvertTo-SecureString "password" -AsPlainText -Force
$creds = New-Object System.Management.Automation.PsCredential($user,$pass)

$url = "http://scorch.domain:81/Orchestrator2012/Orchestrator.svc/Jobs()?`$expand=Runbook&`$filter=(Status eq 'Running') and (Runbook/Name eq '0.1 Runbook B')&`$select=Runbook/Name,Status"
$request = [System.Net.HttpWebRequest]::Create($url)
$request.Credentials = $creds
$request.Timeout = 120000
$request.ContentType = "application/atom+xml,application/xml"
$request.Headers.Add("DataServiceVersion", "2.0;NetFx")
$request.Method = "GET"

$response = $request.GetResponse()
$requestStream = $response.GetResponseStream()
$readStream=new-object System.IO.StreamReader $requestStream
$Output = $readStream.ReadToEnd()
$readStream.Close()
$response.Close()
$Output

#########################################################################################

Here is the output from the request when Runbook B is NOT running:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<feed xml:base="https://scorch.domain:81/Orchestrator2012/Orchestrator.svc/" xmlns:d="http://schemas.microsof
t.com/ado/2007/08/dataservices" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" xmlns="http://
www.w3.org/2005/Atom">
<title type="text">Jobs</title>
<id>https://scorch.domain:81/Orchestrator2012/Orchestrator.svc/Jobs</id>
<updated>2012-02-19T18:59:26Z</updated>
<author>
<name />
</author>
<link rel="self" title="Jobs" href="Jobs" />
</feed>

Here is the output when Runbook B is currently running:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<feed xml:base="https://scorch.domain:81/Orchestrator2012/Orchestrator.svc/" xmlns:d="http://schemas.microsof
t.com/ado/2007/08/dataservices" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" xmlns="http://
www.w3.org/2005/Atom">
<title type="text">Jobs</title>
<id>https://scorch.domain:81/Orchestrator2012/Orchestrator.svc/Jobs</id>
<updated>2012-02-19T19:01:19Z</updated>
<link rel="self" title="Jobs" href="Jobs" />
<entry m:etag="W/&quot;datetime'2012-02-19T19%3A00%3A53.29'&quot;">
<id>https://scorch.domain:81/Orchestrator2012/Orchestrator.svc/Jobs(guid'4f104fcd-d626-421d-9f75-c8c83200
48e0')</id>
<title type="text"></title>
<published>2012-02-19T19:00:49-05:00</published>
<updated>2012-02-19T19:00:53-05:00</updated>
<author>
<name />
</author>
<link rel="edit" title="Job" href="Jobs(guid'4f104fcd-d626-421d-9f75-c8c8320048e0')" />
<link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/Runbook" type="application/atom+xml;type=e
ntry" title="Runbook" href="Jobs(guid'4f104fcd-d626-421d-9f75-c8c8320048e0')/Runbook">
<m:inline>
<entry m:etag="W/&quot;datetime'2012-02-19T03%3A57%3A55'&quot;">
<id>https://scorch.domain:81/Orchestrator2012/Orchestrator.svc/Runbooks(guid'034d6f78-0529-4111-b31
7-24d75fb42493')</id>
<title type="text">0.1 Runbook B</title>
<published>2012-02-19T03:57:20-05:00</published>
<updated>2012-02-19T03:57:55-05:00</updated>
<author>
<name />
</author>
<link rel="edit" title="Runbook" href="Runbooks(guid'034d6f78-0529-4111-b317-24d75fb42493')" />
<category term="Microsoft.SystemCenter.Orchestrator.WebService.Runbook" scheme="http://schemas.microsoft.com/
ado/2007/08/dataservices/scheme" />
<content type="application/xml">
<m:properties>
<d:Name>0.1 Runbook B</d:Name>
</m:properties>
</content>
</entry>
</m:inline>
</link>
<category term="Microsoft.SystemCenter.Orchestrator.WebService.Job" scheme="http://schemas.microsoft.com/ado/2007/0
8/dataservices/scheme" />
<content type="application/xml">
<m:properties>
<d:Status>Running</d:Status>
</m:properties>
</content>
</entry>
</feed>

So you can see in the output above, you can use link logic to find "<d:Status>Running</d:Status>" in the output from the query to determine if 0.1 Runbook B can be started.

Start Runbooks from SharePoint List

After stopping all running/pending runbooks to perform maintenance on the SCOrch environment, it becomes cumbersome to start all runbooks that begin with a monitor date/time activity (especially when the number of runbooks to start climbs above 100….).

You can create a SharePoint list (Figure A) of runbooks that need to be started after maintenance (good to document nonetheless) that Orchestrator can reference to start all of them automatically!

The main field that we need to use is the RunbookID in the SP list.  I also use a field called "Active" in the list to exclude workflows that may not need started in case there is any reason that they shouldn't be started for the time being.

Figure A

The workflow is made up of three runbooks.  A main which queries the SP list for the active runbook ids to start, the execute which starts the targeted runbook, and a seperate runbook to keep track of processed runbooks to later check for any errors.

Main (Figure B)

Figure B

The main starts by resetting a counter to cleanup from previous runs.  Then it uses the Microsoft SharePoint IP by Jeff Fanjoy (more info here http://orchestrator.codeplex.com/releases/view/75877) to query the list identified in the SP IP configuration in options.  Within the properties of the Get Active Runbooks activity, the filter is modified to choose the "Active" field set on the list object (Figure C).

Figure C

From the runbooks found in the list query, it will invoke the execute runbook with the runbook id and runbook name.  It also invokes the counter modification runbook with a parameter – increment (note this invoke must set the property to wait for completion – See Figure E).

Exploring the execute runbook that's invoked in Figure D….it uses the Standard Logging IP (more info here http://orchestrator.codeplex.com/releases/view/76097).  It starts the targeted runbook (you can reference this from my previous post – http://jmattivi.blogspot.com/2012/06/scorch-powershell-to-start-runbook-part.html).  Upon success or failure to start the runbook, it invokes the counter modification runbook with a parameter – decrement (note this invoke must set the property to wait for completion – See Figure E).

Figure D
Figure E

Moving back to the Main runbook (Figure B), it waits for the counter value to be 0 – indicating processing has completed.  If not it hits a timeout after 15 minutes and sends an error email for investigation.  It then queries the Standard Logging table in the database for any failures to start runbooks from the execute runbook.  If errors are found, it uses a Powershell script to parse the flattened data back into multi-valued data and send that information in an email.  If no failures are found, it sends a success email that all targeted scheduled runbooks have been started successfully.

Workflow to Stop all Runbook Jobs

There's not a lot I miss about the Opalis Operator Console compared to the new Orchestration Console….however, there was one feature that is not present in the new OC.  That is the ability to stop all Runbook Jobs.  Whenever there's maintenance that needs to be done to the runbook servers or database, to prevent creating orphaned jobs, all running/pending jobs need to be stopped.

This is a job for the new web service and powershell!  In my previous posts, I showed how to query runbooks that have a status of running or pending and how to stop/cancel a runbook job.  This ties them together creating a workflow right in Orchestrator using a combination of the two to stop all running and pending jobs.

The runbook below queries jobs with a status of running and then invokes the runbook which actually stops the job.  The query returns the necessary properties of the runbook to pass to the stopping job – jobID, RunbookID, and LastModifiedTime.  I have another similar runbook that runs in parallel that queries for pending jobs and calls the same stopping runbook.  These runbooks will loop until the total running/pending jobs = 0.

Be sure to exclude the runbooks that are running this workflow from the query or this runbook will stop itself :) .  Here is an example of the url in the query script to exclude the runbookIDs of the runbooks which run the workflow.

$url = "https://scorch.domain:81/Orchestrator2012/Orchestrator.svc/Jobs()?`$filter=Status eq 'Running' and RunbookId ne guid'10000000-0000-0000-0000-000000000000' and RunbookId ne guid'20000000-0000-0000-0000-000000000000' and RunbookId ne guid'30000000-0000-0000-0000-000000000000'&`$select=Id,RunbookId,LastModifiedTime,Status"

Figure A

Figure B

In Figure A, I reset a counter and then run the powershell script to query for running jobs.  For every running job it finds, it then increments a counter and invokes the stop jobs runbook.  I also decrement the same counter in Figure B so that we know every runbook found was processed successfully.  After the junction, we check to make sure the counter was decremented back to 0.  Then we query for any running jobs again and publish that back to the main runbook that will loop until that is 0.

In Figure B, I implement the Standard Logging IP by Charles Joy to log the stopping script.  This will allow parsing the table for failures and reporting at the end.  As mentioned above, on success or failure, I decrement the counter again to ensure the running job was processed.

Now with one click to start a runbook, you can stop all runbook jobs again!  I had to come up with another workflow to automate starting all necessary jobs again that run with a Monitor Date/Time activity from a Sharepoint list….but that’ll be saved for the next post.

Get all Running Runbook Jobs

This post I'll show how to get all Running Runbook jobs.

I used the ConvertTo-SCOObject function Robert Hearn posted here to parse the xml response from the GET call.

#########################################################################

function ConvertTo-SCOObject {
[CmdletBinding()]
PARAM (
[Parameter(Mandatory=$true)]
[String] $XmlFeed
)
$xml =[xml]$XmlFeed

$ns = New-Object Xml.XmlNamespaceManager $xml.NameTable
$ns.AddNamespace( "d", "http://schemas.microsoft.com/ado/2007/08/dataservices")
$ns.AddNamespace( "m", "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata")
$ns.AddNamespace( "def", "http://www.w3.org/2005/Atom")

$objarray = @()

$entryNodes = $xml.SelectNodes("//def:entry",$ns)
foreach ($entryNode in $entryNodes)
{
$obj = New-Object PSObject
$objectType = $entryNode.SelectSingleNode(".//def:category", $ns).GetAttribute("term")

$obj | add-member Noteproperty -Name "Object Type" -Value $objectType.Split(".")[-1]

$propertiesNode = $entryNode.SelectSingleNode(".//m:properties", $ns)
foreach($propertyNode in $propertiesNode.ChildNodes)
{
$obj | add-member Noteproperty -Name $propertyNode.LocalName -Value $propertyNode.get_InnerText()
}

$links = $entryNode.SelectNodes(".//def:link", $ns)
foreach ($link in $links)
{
$obj | add-member NoteProperty -Name $link.title -Value $link.href
}
$objarray += $obj
}

return $objarray

#########################################################################

Due to the paging limits on the web service collections (Jobs being 500), I used this method for filtering the results.  The most important part of this GET is building the query into the odata URI.  You can build in filters, select statements, etc. on the Web Service collections shown like this.

http://scorch.domain:81/Orchestrator2012/Orchestrator.svc/Jobs()?$filter=Status eq 'Running'
\_______________________________________________________/ \____/  \_________________________/
                           |                                |                   |
                    service root URI                  resource path        query options

Putting this into practice w/ SCOrch, you can query the Jobs collection, filter for Jobs that have a Status of "Running", and select the fields you would like to return – Id,RunbookId,LastModifiedTime,Status.

"http://scorch.domain:81/Orchestrator2012/Orchestrator.svc/Jobs()?`$filter=Status eq 'Running'&`$select=Id,RunbookId,LastModifiedTime,Status"

So you can use this on any of the collections to filter results i.e. Jobs, Runbooks, RunbookInstances, Statistics, etc.

The GET request would look like this.

$creds = Get-Credential("domain\username")

$XmlFeed = $null
$count = 0
$url = "http://scorch.domain:81/Orchestrator2012/Orchestrator.svc/Jobs()?`$filter=Status eq 'Running'&`$select=Id,RunbookId,LastModifiedTime,Status"
$request = [System.Net.HttpWebRequest]::Create($url)
$request.Credentials = $creds
$request.Timeout = 60000
$request.ContentType = "application/atom+xml,application/xml"
$request.Method = "GET"

$response = $request.GetResponse()
$requestStream = $response.GetResponseStream()
$readStream=new-object System.IO.StreamReader $requestStream
$XmlFeed = $readStream.ReadToEnd()
$readStream.Close()
$response.Close()

Using the ConvertTo-SCOObject function, you can then parse the response and set your variables and get a count of the running runbooks.

ConvertTo-SCOObject -XmlFeed $XmlFeed | %{$count++}
$Id = ConvertTo-SCOObject -XmlFeed $XmlFeed | %{$_.Id}
$RunbookId = ConvertTo-SCOObject -XmlFeed $XmlFeed | %{$_.RunbookId}
$LastModifiedTime = ConvertTo-SCOObject -XmlFeed $XmlFeed | %{$_.LastModifiedTime}
$Status = ConvertTo-SCOObject -XmlFeed $XmlFeed | %{$_.Status}

Here is the complete script for getting all Running Runbook Jobs.  Highlighted fields would need to be updated per your environment.

##########################################################################################################
function ConvertTo-SCOObject {
[CmdletBinding()]
PARAM (
[Parameter(Mandatory=$true)]
[String] $XmlFeed
)
$xml =[xml]$XmlFeed

$ns = New-Object Xml.XmlNamespaceManager $xml.NameTable
$ns.AddNamespace( "d", "http://schemas.microsoft.com/ado/2007/08/dataservices")
$ns.AddNamespace( "m", "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata")
$ns.AddNamespace( "def", "http://www.w3.org/2005/Atom")

$objarray = @()

$entryNodes = $xml.SelectNodes("//def:entry",$ns)
foreach ($entryNode in $entryNodes)
{
$obj = New-Object PSObject
$objectType = $entryNode.SelectSingleNode(".//def:category", $ns).GetAttribute("term")

$obj | add-member Noteproperty -Name "Object Type" -Value $objectType.Split(".")[-1]

$propertiesNode = $entryNode.SelectSingleNode(".//m:properties", $ns)
foreach($propertyNode in $propertiesNode.ChildNodes)
{
$obj | add-member Noteproperty -Name $propertyNode.LocalName -Value $propertyNode.get_InnerText()
}

$links = $entryNode.SelectNodes(".//def:link", $ns)
foreach ($link in $links)
{
$obj | add-member NoteProperty -Name $link.title -Value $link.href
}
$objarray += $obj
}

return $objarray
}

$creds = Get-Credential("domain\username")

$XmlFeed = $null
$count = 0
$url = "http://scorch.domain:81/Orchestrator2012/Orchestrator.svc/Jobs()?`$filter=Status eq 'Running'&`$select=Id,RunbookId,LastModifiedTime,Status"
$request = [System.Net.HttpWebRequest]::Create($url)
$request.Credentials = $creds
$request.Timeout = 60000
$request.ContentType = "application/atom+xml,application/xml"
$request.Method = "GET"

$response = $request.GetResponse()
$requestStream = $response.GetResponseStream()
$readStream=new-object System.IO.StreamReader $requestStream
$XmlFeed = $readStream.ReadToEnd()
$readStream.Close()
$response.Close()

ConvertTo-SCOObject -XmlFeed $XmlFeed | %{$count++}
$Id = ConvertTo-SCOObject -XmlFeed $XmlFeed | %{$_.Id}
$RunbookId = ConvertTo-SCOObject -XmlFeed $XmlFeed | %{$_.RunbookId}
$LastModifiedTime = ConvertTo-SCOObject -XmlFeed $XmlFeed | %{$_.LastModifiedTime}
$Status = ConvertTo-SCOObject -XmlFeed $XmlFeed | %{$_.Status}
##########################################################################################################

A few good resources for working with the Orchestrator Web Service.

http://blogs.technet.com/b/scorch/archive/2011/06/17/fun-with-the-orchestrator-2012-beta-the-web-service-and-powershell.aspx

http://www.odata.org/developers/protocols/uri-conventions

Stopping (Canceling) a Runbook Job w/ Powershell

Canceling a runbook job requires a few more options in the headers and payload of the request.  There are three properties of the job that you must use to  cancel the runbook.

$JobID = "10000000-0000-0000-0000-000000000000"
$RunbookID = "00000000-1000-0000-0000-000000000000"
$LastModifiedTime = "2012-01-10T13:44:36.823"

First, the url must point to the specific job guid that you intend to stop.

$url = "http://scorch.domain:81/Orchestrator2012/Orchestrator.svc/Jobs(guid'$JobID')"

Second, the request headers also require a few more options than a regular GET or POST to start a Runbook.

$request.Headers.Add("X-HTTP-Method", "MERGE")
$request.Headers.Add("If-Match", 'W/"datetime' + "'" + $LastModifiedTime + "'" + '"')

Third, the payload needs to include these fields in the properties struct:  JobID, RunbookID, and Status = Canceled.

"<m:properties>" + "`n" +
'<d:Id m:type="Edm.Guid">' + $JobID + '</d:Id>' + "`n" +
'<d:RunbookId m:type="Edm.Guid">' + $RunbookID + '</d:RunbookId>' + "`n" +
"<d:Status>Canceled</d:Status>" + "`n" +
"</m:properties>" + "`n" +

Here is the complete script to cancel a specific Job.  Fields in red would need to be updated per your environment.

###############################################################################################################
$creds = Get-Credential("domain\username")

$JobID = "10000000-0000-0000-0000-000000000000"
$RunbookID = "00000000-1000-0000-0000-000000000000"
$LastModifiedTime = "2012-01-10T13:44:36.823"

$url = "http://scorch.domain:81/Orchestrator2012/Orchestrator.svc/Jobs(guid'$JobID')"
$request = [System.Net.HttpWebRequest]::Create($url)
$request.Credentials = $creds
$request.Timeout = 60000
$request.Accept = "application/atom+xml,application/xml"
$request.Headers.Add("Accept-Charset", "UTF-8")
$request.Headers.Add("X-HTTP-Method", "MERGE")
$request.Headers.Add("If-Match", 'W/"datetime' + "'" + $LastModifiedTime + "'" + '"')
$request.ContentType = "application/atom+xml"
$request.Method = "POST"

$requestBody = '<?xml version="1.0" encoding="utf-8" standalone="yes"?>' + "`n" +
'<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">' + "`n" +
'<content type="application/xml">' + "`n" +
"<m:properties>" + "`n" +
'<d:Id m:type="Edm.Guid">' + $JobID + '</d:Id>' + "`n" +
'<d:RunbookId m:type="Edm.Guid">' + $RunbookID + '</d:RunbookId>' + "`n" +
"<d:Status>Canceled</d:Status>" + "`n" +
"</m:properties>" + "`n" +
"</content>" + "`n" +
"</entry>"

$requeststream=new-object System.IO.StreamWriter $request.GetRequestStream()
$requeststream.Write($requestBody)
$requeststream.Flush()
$requeststream.Close()

$response=$request.GetResponse()
$requestStream=$response.GetResponseStream()
$readStream=new-object System.IO.StreamReader $requestStream
$Output=$readStream.ReadToEnd()
$readStream.Close()
$response.Close()
#######################################################################################################

SCOrch – Powershell to start Runbook – Part 2

Here is an example to start a Runbook w/ parameters from Powershell.  It’s pretty much the same exact as a standard POST to start a Runbook, but also includes the parameters in the properties struct.

I’ve found that the free program Fiddler helps to find the guids and struct needed to build the POST.  You can also use the Orchestration Console to drill down through to the Instance Summary and Activity Details to find the guids for each parameter.

This is the parameters struct that is added to the regular POST.

Parameter1/Parameter2 are the names that you give the parameter.

You need to specify the guid for each parameter.

Value1/Value2 are the input values you’d like to specify for the Initialize Data activity.

<d:Parameters>&lt;Data&gt;&lt;Parameter&gt;&lt;Name&gt;Parameter1&lt;/Name&gt;&lt;ID&gt;{10000000-0000-0000-0000-000000000000}&lt;/ID&gt;&lt;Value&gt;Value1&lt;/Value&gt;&lt;/Parameter&gt;&lt;Parameter&gt;&lt;Name&gt;Parameter2&lt;/Name&gt;&lt;ID&gt;{20000000-0000-0000-0000-000000000000}&lt;/ID&gt;&lt;Value&gt;Value2&lt;/Value&gt;&lt;/Parameter&gt;&lt;/Data&gt;</d:Parameters>

Here is the complete script to include the parameters in the POST to start the Runbook.  The highlighted fields would need updated to your environment.

 ##############################################################################

$creds = Get-Credential("domain\username")

$url = "http://scorch.domain:81/Orchestrator2012/Orchestrator.svc/Jobs/"

$request = [System.Net.HttpWebRequest]::Create($url)

$request.Credentials = $creds

$request.Timeout = 60000

$request.Accept = "application/atom+xml,application/xml"

$request.Headers.Add("Accept-Charset", "UTF-8")

$request.ContentType = "application/atom+xml"

$request.Method = "POST"

$requestBody = '<?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:Parameters>&lt;Data&gt;&lt;Parameter&gt;&lt;Name&gt;Parameter1&lt;/Name&gt;&lt;ID&gt;{10000000-0000-0000-0000-000000000000}&lt;/ID&gt;&lt;Value&gt;Value1&lt;/Value&gt;&lt;/Parameter&gt;&lt;Parameter&gt;&lt;Name&gt;Parameter2&lt;/Name&gt;&lt;ID&gt;{20000000-0000-0000-0000-000000000000}&lt;/ID&gt;&lt;Value&gt;Value2&lt;/Value&gt;&lt;/Parameter&gt;&lt;/Data&gt;</d:Parameters>

<d:RunbookId type="Edm.Guid">00000000-0000-0000-0000-000000000000</d:RunbookId>

</m:properties>

</content>

</entry>

'

$requeststream=new-object System.IO.StreamWriter $request.GetRequestStream()

$requeststream.Write($requestBody)

$requeststream.Flush()

$requeststream.Close()

$response=$request.GetResponse()

$requestStream=$response.GetResponseStream()

$readStream=new-object System.IO.StreamReader $requestStream

$Output=$readStream.ReadToEnd()

$readStream.Close()

$response.Close()

##############################################################################

SCOrch – Powershell to start Runbook – Part 1

I have seen a few examples of ways to start a runbook with C and VB.  However, I haven’t come across any ways of using Powershell to start a runbook successfully.

This will be the first post of a series of different ways to interact w/ the web service through Powershell.  I’ll also post how to start w/ parameters, stop a runbook, get all running/pending runbooks, etc….

Here’s the short of it.  I Haven’t built any logic, parameters, or error handling into it yet….just wanted to get the POST to work with Orchestrator’s web service.

You would need to update the credentials, url for the web service pointed to the Jobs collection, and the RunbookID in the request body (highlighted below).

##############################################################################
$creds = Get-Credential("domain\username")
$Output = $null
$Success = $null
$url = "http://scorch.domain:81/Orchestrator2012/Orchestrator.svc/Jobs/"
$request = [System.Net.HttpWebRequest]::Create($url)
$request.Credentials = $creds
$request.Timeout = 36000
$request.Accept = "application/atom+xml,application/xml"
$request.Headers.Add("Accept-Charset", "UTF-8")
$request.ContentType = "application/atom+xml"
$request.Method = "POST"

#Request w/o Parameters
$requestBody = '<?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">RunbookGUID</d:RunbookId>
</m:properties>
</content>
</entry>
'

$requeststream=new-object System.IO.StreamWriter $request.GetRequestStream()
$requeststream.Write($requestBody)
$requeststream.Flush()
$requeststream.Close()
$response=$request.GetResponse()
$requestStream=$response.GetResponseStream()
$readStream=new-object System.IO.StreamReader $requestStream
$Output=$readStream.ReadToEnd()
$readStream.Close()
$response.Close()

$Output
##############################################################################