With Service Management Automation (SMA), the new capability in Windows Azure Pack and Orchestrator 2012 R2, creating and running runbooks are core tasks for IT professionals who are automating repetitive and complex tasks and integrating across systems. SMA uses the PowerShell workflow engine to run runbooks, which means that runbooks are created as PowerShell workflows (see the Runbook Concepts article for an introduction).

As you create SMA runbooks, you will want to be aware of and follow best practices in order to get the most out of the system. Creating runbooks involves an understanding of PowerShell, PowerShell Workflow, and SMA. At the end of the day, if you are familiar with PowerShell, then creating SMA runbooks is straightforward.

In this post, I will cover a few of the basic concepts that will help you create high-quality runbooks. Other posts in this series will cover other useful aspects of runbook creation and management. The concepts covered in this post are the following:

  • Defining input parameters
  • Defining output type
  • Calling other runbooks from within a runbook

Defining Input Parameters

Most runbooks require some input data to be provided before execution. Thus, it is common during runbook authoring to define the input parameters that will be needed. The main attributes that define an input parameter are these:

  • Name
  • Type (.Net type)
  • Default value (if any)
  • Mandatory (or not)

SMA supports these attributes of input parameters for runbooks. PowerShell supports many more attributes of input parameters such as validation, aliases, and parameter sets; however SMA currently supports only the list above.

The screenshot below shows parameters defined in a runbook. (For more illustration you can also look at the Sample-Using-RunbookParameters runbook that is pre-installed with SMA.)

Example parameter definitions

Using this example, to add a user who works for the company Contoso and has admin rights you would submit these PowerShell commands:

Example calling runbook with parameters

In some cases, the [switch] parameter type can be useful in SMA runbooks; however, there are some cases where it is simpler to use the [boolean] type. The reasons for using one or the other are explained in this blog post.

In PowerShell Workflow when you call an activity or another workflow (runbook) all parameters must be referenced by name and not position. This restriction does not apply to cmdlets which allow positional parameters.

Best Practice: As a best practice, you should be very explicit when declaring input parameters to your SMA runbooks. Take the example above as a guide and always include the parameter Type, Name, and whether it is Mandatory or not. Note that if you set a default value for a parameter, then it will be an optional parameter regardless of how you set the Mandatory attribute. If you leave out the Mandatory attribute then by default the parameter is optional; however, it is always best to explicitly declare this. When you name a parameter use letters, numbers, and underscore characters (don’t use the hyphen character because parameter names with hyphens need special handling that you want to avoid).

If you want to be able to start your runbooks from the SMA portal using the Start Runbook wizard UI then keep these things in mind about input parameters:

  • The wizard UI allows you to input values for runbook parameters that can be represented by number, string, datetime, switch, boolean, SMA credential asset name, JSON array, or JSON object.
  • If a runbook has a parameter with a default value, this default value will not appear in the UI; however, if you leave the parameter blank in the UI, then the default value will be used when the runbook runs (we are looking at improving this experience in a later release).
  • If a runbook parameter takes an [array] or [object] type, then these must be passed in JSON format in the start runbook dialog. For example:
    • A parameter of type [object] that expects a property bag can be passed in the UI with a JSON string formatted like this: {“StringParam”:”Joe”,”IntParam”:42,”BoolParam”:true}.    
    • A parameter of type [array] can be input with a JSON string formatted like this: [“Joe”,42,true].
  • If a runbook parameter takes a PSCredential type then you need to pass the string name of a SMA credential asset. Behind the scenes, the credential asset with that name will be retrieved and passed to the runbook.

Example of entering runbook parameters in SMA UX

If you want to start a runbook from within a runbook using the Start-SmaRunbook cmdlet (more about this cmdlet later in this post) then keep these things in mind about input parameters (and see the code example below):

  • Input parameters to the runbook that is started by Start-SmaRunbook activity are passed in a hashtable as key/value pairs, where key is parameter name and value is value of the parameter.
  • The runbook that is started by Start-SmaRunbook should have input parameters that can be represented by number, string, datetime, switch, boolean, SMA credential asset name, JSON array, or JSON object.
  • If an input parameter is a PSCredential type you need to pass the string name of a SMA credential asset. Behind the scenes, the credential asset with that name will be retrieved and passed to the runbook.
  • If an input parameter is a switch type then the parameter needs to be declared in the hashtable with a Boolean value: $true or $false. For example, if HasAdminRights was a [switch] parameter it would be declared like the example below. It would also be declared this same way if it was a Boolean parameter.
  • If the input parameter is a property bag, then define it as [object] type in the called runbook and pass a PowerShell hashtable object as the input value. For example, see the $name parameter below.
  • If the input parameter is a complex object (e.g., [System.Diagnostics.Process]), then define it as [object] type in the called runbook and pass the complex object as the input value.

Example of parameter input for runbook started with Start-SmaRunbook

Defining Output Type

PowerShell has long supported the definition of output type in functions and cmdlets with the use of the OutputType attribute. This attribute has no effect during runtime, and instead has been provided as a way for tools to learn, at design time, the object types that cmdlets output without having to run them.

OutputType can be used in PowerShell workflow, and you should include OutputType in runbooks that have output. Place the OutputType attribute near the top of the runbook, just before any parameter declarations. Here is an example of the use of OutputType in a runbook.

Example of OutputType in runbooks

As SMA evolves, and the toolset for creating runbooks is expanded and enhanced, it will be important that cmdlets and runbooks include the OutputType definition and then adhere to that contract.

Best Practice: This is an easy best practice to follow: always include OutputType in your cmdlets and runbooks that have output.

Calling Other Runbooks from a Runbook

One of the best practices when creating code of any kind is modularization: creating discrete, reusable code units. For SMA this means putting self-contained tasks within cmdlets and runbooks, and then re-using those in many runbooks. Thus, it could be common practice for a parent runbook to call one or more child runbooks as part of the process being executed.

There are two ways to call child runbooks in SMA:

  • Invoke inline
  • Start with Start-SmaRunbook

As a side note, we use the general terms “nested” or "child" for any child runbook that is called from a parent runbook.  PowerShell uses the verb “invoke” for operations that are initiated and run synchronously and the verb “start” for operations that are initiated and run asynchronously; we will follow this precedent and use the verbs “invoke” and “start” for synchronous and asynchronous child runbooks, respectively.

Invoke a Runbook Inline

Runbooks that are invoked inline run in the same job as the parent runbook.  This means that the parent workflow will wait for a child runbook to finish (synchronous) before continuing with the next part of the process.  This also means that all exceptions thrown by the child runbook and all stream output produced by the child runbook are going to be associated with the parent job; therefore all tracking of child runbook execution and output will be through the job associated with the parent runbook.

When you start a runbook that has child runbooks invoked inline, the child runbooks (and all descendants) get compiled into the parent runbook before execution starts.  During this compilation phase the parent runbook is parsed for the names of child runbooks; this parsing happens recursively through all descendant runbooks.  When the complete list of runbooks is obtained the scripts for these runbooks are retrieved from storage and assembled into a single file that is passed to the PowerShell workflow engine.  For this reason, at the time a runbook job is submitted the parent and descendant runbooks must already be published otherwise an exception will occur during compilation.  Currently, the order of publishing also matters: you must publish the child runbooks first and then publish the parent.  Similarly, when testing the parent runbook in the SMA authoring page you must first publish the child runbooks and then you can test the parent.  Another consequence of this is that you cannot use a variable to pass the name of an child runbook invoked inline: you must always explicitly name the child runbook within the parent runbook.

Here is an example of a runbook that invokes two child runbooks inline: one child runbook returns output and the other child runbook does not return output.

Example code to invoke child runbooks inline

Start a Runbook with Start-SmaRunbook

You can start a runbook in a separate job by using the Start-SmaRunbook cmdlet (which is in the Microsoft.SystemCenter.ServiceManagementAutomation module that comes installed as part of SMA). When you start a child runbook using Start-SmaRunbook a call is made to the SMA web service to start the runbook and a new job is created.

When a child runbook is started in this way the parent runbook does not wait for the child runbook to finish before continuing (asynchronous). This approach is useful in cases when a parent runbook wants to spin off processes and then forget about them. However, it does come at the cost of creating more jobs in the system, which can make troubleshooting somewhat more involved; and it is also more involved if you need to get back output from the child runbook.

Below is an example of a runbook that uses Start-SmaRunbook to start child runbooks:

Example code to start a child runbook

When you start a child runbook using Start-SmaRunbook the return value is the job id. If you want return data from the child runbook then you need to do work using the job id to learn when the job completes and then extract any output. Here is an example of script that can help you with this (note that for illustration clarity the calls for the Sma cmdlets are not complete in this example and will require connection information). Keep in mind that the child job may not be able to start for some time if there are too many long running jobs in the system already; in this case the loop described below may never complete, so you may want to add some limiting factor such as a timeout after a certain amount of time passes. Also note that if you start several child jobs the system does not guarantee the order in which the jobs will be started.

Example code to get child job output from started runbook

Comparison of Child Runbooks that are Invoked Inline versus Started with Start-SmaRunbook

Invoked Inline

  • Pro
    • Parent and child runbooks run in the same job, so there are fewer jobs in the system, which allows other runbooks to run.
    • The parent runbook waits for the child runbook to finish before continuing, so the parent runbook can directly get any return data from the child.
    • Exceptions thrown by the child runbook and stream output produced by the child are associated with the parent job, which can make troubleshooting easier, because there is only one job to investigate.
    • Runbook input parameters can be of any type, primitive to complex.
  • Con
    • The parent runbook must wait for the child runbooks to complete, which can increase the overall time for the parent runbook to complete.
    • You cannot use a variable or parameter in the parent runbook to pass in the name of an inline child runbook.
    • The child runbook must be published before the parent runbook is published.

Started with Start-SmaRunbook

  • Pro
    • The parent and child runbooks run in different jobs, which allows the parent to spin off multiple jobs that can run in parallel.
    • The parent runbook does not wait for the child runbooks, and so can continue processing while the child runbooks run.
    • You can use a parameter or variable in the parent runbook to pass in the name of the runbook to invoke.
  • Con
    • Parent and child runbooks run in different jobs, so exceptions and stream output are stored separately and must be tracked down separately, which may make troubleshooting more difficult.
    • More jobs are created in the system which can cause job limits to be reached and block other runbooks from running.
    • Getting return data from a child runbook job is not straightforward.
    • Runbook input parameters are restricted to primitive types, array, and object, because they must survive the JSON serialization of objects that occurs with the call through the web service.

Summary

SMA is a great tool that allows you to create runbooks that can take advantage of the numerous beneficial features of the PowerShell workflow engine. With an understanding of some of the inner workings of SMA and PowerShell workflow and of some best practices you can create high-quality, reliable, and maintainable runbooks. In this post, we have discussed several best practices for defining input parameters, for defining runbook output, and for calling child runbooks. Now that you have read through the information, take the time to experiment with a few simple runbooks in SMA using the examples above so that the concepts presented in this post become obvious to you. Thanks for listening, and have fun putting this new knowledge into practice.