Quick Tips and Tricks for Runbook Writing
Introduction
This article outlines tips for writing runbooks in Service Management Automation and Azure Automation.
Most of these tips apply to both services. If the tip doesn’t apply to both, the tip will explicitly call out SMA or Azure Automation. When you see the term Automation used, the tip refers to both. These tips do not go into detail about why we suggest you follow this guidance. For more detail on PowerShell Workflow and runbook, look at our runbook authoring documentation and PowerShell Workflow introduction guide.
Also check out this PowerShell Workflow Gotchas wiki that highlights some of the most frequently problems that SMA and Azure Automation users run into.
General Guidance
DO
- use verbs from the approved verbs list when naming your runbooks.
- give the same name to .ps1 files and the workflow they contain.
- follow the naming convention for PowerShell cmdlets when you name your runbooks / PowerShell workflows: <ApprovedVerb>-<Noun>.
- test your workflow in Automation, especially if you authored it in a local environment first.
- explicitly define which parameters are and are not mandatory.
CONSIDER
- using an InlineScript block if you're going to use the pipeline.
AVOID
- strongly typing runbook parameters which are not primitive types.
- interacting with or depending upon local server resources on the Runbook Worker if you are using SMA.
- relying on any resources you yourself have not copied to the runbook host in Azure Automation. If you need to use a file, checkpoint then copy it to the host to ensure it will be where you expect it.
- using the pipeline (i.e. chaining cmdlets with the “|” character) in PowerShell Workflow, unless you understand that a complex object is not being passed through the pipeline.
- creating runbooks that are expected to execute for an excessively long time period. If you want a monitor runbook to run all the time, start a new instance of it by calling Start-SMARunbook or Start-AzureAutomationRunbook before the job completes.
DO NOT
- use switch parameters on PowerShell workflows (use a Boolean parameter type instead).
- use positional parameters to cmdlets.
- use interactive commands or commands that expect a console. For example: Write-Host
Comments
Prefix your runbooks with an explanatory comment block following MSFT guidance
Make sure to include the following labels:
- .SYNOPSIS – provide an quick overview of the workflow’s functionality
- .DESCRIPTION – provide a more in depth description of the workflow’s functionality
- .PARAMETER – provide a parameter section for each of your parameters and include the parameter name, type and brief description
- .EXAMPLE – provide an example of using your workflow
- .NOTES – add any additional that might aid understanding of the workflow
CONSIDER
- using write-verbose statements instead of / in addition to comments to provide instrumentation for troubleshooting.
- using #region and #endregion tags to organize long scripts.
Using InlineScript
DO
- use the same variable name when bringing values inside an InlineScript block with the $using directive.
- use an InlineScript block if you need to call a method on an object returned by a cmdlet.
- be aware that Automation modules are not available if you are connecting to local host with an InlineScript block (unless they are explicitly installed on the local host).
CONSIDER
passing in your verbose/error action preferences when executing an inline script on a remote host or locally with alternate credentials.
Example:
$VerbosePreference = [System.Management.Automation.ActionPreference]$Using:VerbosePreference
$ErrorActionPreference = [System.Management.Automation.ActionPreference]$Using:ErrorActionPreference
placing all $using directives at the top of an InlineScript block.
AVOID
- passing complex/large objects (e.g. large hash tables) to InlineScripts or using them as return values from an InlineScript block.
- writing entire workflows inside an InlineScript block.
- passing large amounts of data (greater than 5 MB) to an InlineScript via the $using Directive.
DO NOT
- specify credentials to an InlineScript block without also specifying a computer name (even if it is localhost).
Parallel Execution
CONSIDER
- using Start-SmaRunbook or Start-AzureAutomationRunbook to do "parallel" invocations
AVOID
- parallel execution unless tasks to be executed in parallel are independent of each other.
Checkpoints
DO
- use checkpoints to save state at crucial points of your runbook.
- ensure that workflows can safely restart from a checkpoint if interrupted.
- retrieve credentials again from the Automation asset store after a checkpoint.
CONSIDER
- creating a checkpoint after activities that should not be repeated (i.e. actions that are not idempotent).
- creating a checkpoint before activities that are susceptible to failure.
- creating a checkpoint after long-running or expensive activities that should not repeated due to cost.
- setting variables to $null before creating a checkpoint if they contain a large amount of data that does not need to be persisted.
AVOID
- using checkpoints that require a large amount of data (5 MB) to be persisted to the database.
Logging
DO
- log messages about workflow progress and state using the cmdlets Write-Progress and Write-Verbose
- use the Runbook Configuration page to turn on Verbose or Progress streams to help debug a production runbook
- use the Write-Verbose cmdlet for a higher level of detail
- disable verbose/progress logging when done troubleshooting or developing a runbook.
CONSIDER
- including Write-Verbose statements to log values retrieved from the Automation asset store. Except values that should not be stored in plain text!!!!
- log a start and stop message at the beginning/end of each workflow/function.
AVOID
- enabling the “Log Progress” setting.
- enabling Debug/Verbose logging in a Production environment unless troubleshooting an issue.
DO NOT
- use Write-Debug
- use the Write-Object cmdlet to log debug messages
Local Testing and Debugging
CONSIDER
- using the Emulated Automation Activities module to enable local testing and debugging.
- reading about local runbook authoring.
Error Handling
CONSIDER
using $ErrorActionPreference = "Stop" or $ErrorActionPreference = "Suspend" for runbooks that should not continue to run if they hit an error
using try catch and notifying the admin via email if there is an error or exception in a business critical runbook
using the following pattern to log non-terminating errors encountered in a workflow.
Write-Error -Message $_.Message -ErrorAction Continue
using cmdlets to get job output filtered in a human readable and sortable format:
$JobOutput = Get-SmaJobOutput -Id $JobId -WebServiceEndpoint $WebServiceEndpoint
OR
$JobOutput = Get-AzureAutomationJob –Id $JobId –Account $MyAccount
creating a common function or runbook to handle error routing
Variables
DO
- use variables in the Automation asset store for values that are expected to change between environments.
CONSIDER
- loading all values from the Automation asset store at the beginning of your runbook.
- using CredSSP for authentication when invoking an InlineScript on localhost in SMA.
AVOID
- using “magic strings” or unexplained, hard-coded values in runbooks.
DO NOT
- store any secrets (credentials, certificates, etc.) anywhere in plain text. Use the asset store instead.
Monitors
DO
- separate monitor runbooks and user-interface specific functionality (e.g. fetching or updating information in SharePoint) from runbooks designed to actually complete automated tasks.
CONSIDER
- consolidating monitors to conserve runbook worker capacity if you are using SMA.
PowerShell Modules
DO
- install modules in the SMA environment using the documented process. See the Importing A Module section on TechNet or install the module on every runbook worker.
- define connections for any modules that communicate with external systems.
Modularity
DO
- define any functions within the workflow definition.
- treat functions as if they were InlineScript blocks.
- use try/catch statements where necessary.
CONSIDER
- encapsulating common code
- within a function if it will be called only within the current workflow.
- in a separate workflow if it will be called by many different workflows and it contains business logic (e.g. business rules governing creation of an AD group).
- in a PowerShell module if it will be called by many different workflows and it does not contain business logic (e.g. functions for creating Incidents in Remedy).
- breaking apart lengthy workflows into logical components.