Update Runbook GUIDs Script for SCSM
By Gabriel Taylor
Published June 26, 2014
Estimated Reading Time: 8 minutes

Update Runbook GUIDs Script for SCSM

I spend a lot of my time assisting clients with Service Manager and helping deliver self-service automation capabilities to organizations. Two things often involved in that task are employing automation via Orchestrator Runbooks synced to Service Manager, and migrating Runbooks and management packs between Development and Production environments. If you’re like me and have combined those tasks in your work, you’ve probably run into headaches with the migration of management packs containing the templates and request offerings leveraging those Runbooks.

 

THE PROBLEM

The process of migrating management packs containing references to synced Runbooks is complicated by the format of those references. Runbook references are stored not by a property synced from Orchestrator but by the GUID generated by Service Manager upon initial sync. Since the GUID is unique to a given Service Manager environment, importing a management pack without updating the Runbook GUID leaves the reference incorrect, the template and request offering broken, and the automation not working.

 

The issue isn’t a secret, and there have been blogs discussing it, as well as third party tools designed to help ease the process. Unfortunately, those instructions and tools have been limited to manual copy/paste solutions or those that only update a single management pack at a time and necessitate re-inputting configuration information each time. In my experience, the faster and more automated a solution can be, the more valuable it is, both to me and to my clients. I decided I should find a better solution to update Runbook GUIDs.

 

THE SOLUTION

The work involved to collect and update Runbook GUIDs in the management packs is incredibly repetitive and time consuming, making it perfect for scripted automation.
I created a PowerShell script to automatically process a collection of unsealed management packs to update Runbook GUIDs. Not only does it automate the entire process, but it completes the work fast. The full script is described in detail below, as well as provided as a download at the end of this post for your use.

 

The script is designed to connect to both a Source and a Target Service Manager environment, generating lists of the synced Runbook Items in each. For each Runbook Item it finds that exists in both environments, the script searches through the content of all unsealed management pack files (.xml) in a specified folder to find any instances of Source environment Runbook GUIDs. If it finds them, it replaces them with the GUID of the appropriate Runbook Item from the Target environment. Once each of the matching Runbooks have been processed, the resulting management packs can be imported into the Target environment while preserving all automation configuration.

 

HOW TO USE THE SCRIPT

The script is designed to be run with parameters, so to use it, you simply need to run the script from a PowerShell prompt with the appropriate values for your environment. Alternatively, you can edit the parameters’ default values to hard-code them for whatever is appropriate in your environment and launch it from File Explorer. The only mandatory parameters are the Source and Target Service Manager servers. Unless otherwise specified, the script assumes that the Service Manager native PowerShell module can be imported from its default install path on the C drive and that all unsealed management packs to be processed can be found in a folder titled “Unsealed MPs” located in the same folder as the script. If your unsealed management pack files are in a different location, you will need to specify the alternative location. The intended usage from the command line is this:

 

UpdateRunbookGUIDs.ps1 -SourceSMserver “” -TargetSMserver “” -SMInstallPath “” -UnsealedMPFolder “”

 

BREAKING DOWN THE SCRIPT

The script is heavily commented and should be easy to read. In addition to the script’s comments, the following paragraphs will break each section down in more detail.

## Parameter Declaration
param (
    [parameter(Mandatory=$true)][string]$SourceSMserver,
    [parameter(Mandatory=$true)][string]$TargetSMserver,
	[string]$SMInstallPath = "C:\Program Files\Microsoft System Center 2012 R2\Service Manager",
	[string]$UnsealedMPFolder = (get-location).path + "\Unsealed MPs\"
	)

 

This section declares our four parameters and sets default values for two of them. As stated above, the script defaults the $SMInstallPath varuable to the default System Center 2012 R2: Service Manager install directory, and assumes all unsealed management pack files are stored in a subfolder of the working directory titled, “Unsealed MPs”.

## Clear error variable
$error.clear()

## Variable Declaration
$SMModule = $SMInstallPath + "\Powershell\System.Center.Service.Manager.psd1"
$RBItemClass = "Microsoft.SystemCenter.Orchestrator.RunbookItem"

 

This next section clears the error variable to ready it for storing any errors in the script, then defines the location of the Service Manager native PowerShell module (based on the provided $SMInstallPath variable) and declares the internal name of the Runbook Item class within Service Manager. Whenever Runbooks are synced from Orchestrator to Service Manager, their information is stored within instances of the Runbook Item class. This value is the internal name of that classtype and is necessary to generate our lists of synced Runbooks.

## Test the Unsealed MP Folder path to confirm it exists and was entered correctly
If((Test-Path $UnsealedMPFolder) -eq $false)
	{
	Write-Host "Unsealed Management Pack folder is invalid; $UnsealedMPFolder not found." -ForegroundColor Yellow -BackgroundColor DarkBlue
	Write-Host "Please supply a valid path to the folder containing your unsealed MPs via the -UnsealedMPFolder switch." -ForegroundColor Yellow -BackgroundColor DarkBlue
	Write-Host "Exiting script." -ForegroundColor Yellow -BackgroundColor DarkBlue
	Exit
	}

 

This section validates the provided unsealed management pack folder location. If the folder path provided does not exist, the script will notify you that a correct folder path must be provided and will exit, allowing you to re-launch the script with the correct folder declared via the -UnsealedMPFolder parameter.

## Check whether or not SCSM PowerShell modules have been imported and, if not, import them
Write-Host "Checking whether or not Service Manager PowerShell module is loaded ..." -ForegroundColor Green -BackgroundColor DarkBlue
$ExistingModules = (get-module|%{$_.name}) -join " "
If(!$ExistingModules.Contains("System.Center.Service.Manager"))
	{
	Write-Host "Service Manager PowerShell module not found.  Importing Service Manager PowerShell module ..."
	If((Test-Path $SMInstallPath) -eq $false)
		{
		Write-Host "Invalid Service Manager install directory; $SMInstallPath not found." -ForegroundColor Yellow -BackgroundColor DarkBlue
		Write-Host "Please supply a valid Service Manager install directory via the -SMInstallPath switch." -ForegroundColor Yellow -BackgroundColor DarkBlue
		Write-Host "Exiting script." -ForegroundColor Yellow -BackgroundColor DarkBlue
		Exit
		}
	Else
		{
		Import-Module $SMModule -Force
		}
	Write-Host "Service Manager PowerShell module imported."
	}
Else 
	{
	Write-Host "Service Manager PowerShell module already loaded."
	}

 

The next section first determines whether or not the native Service Manager PowerShell module has already been imported. If it has (for example, if you are running this script from a Service Manager Shell), the script will continue on. If the module is not found, the script will test the $SMInstallPath variable to confirm the folder exists and, if it does, will import the native module. If the folder path does not exist (for example, if Service Manager is installed to a non-default location), the script will notify you of the issue and exit, allowing you to re-launch the script with the correct folder declared via the -SMInstallPath parameter.

## Get the list of active runbook CIs in Source Service Manager
Write-Host "Connecting to $SourceSMserver and identifying the list of active Runbook Items ..." -ForegroundColor Green -BackgroundColor DarkBlue
$SourceRBclass = Get-SCClass -Name $RBItemClass -ComputerName $SourceSMserver
$SourceRBInstances = Get-SCClassInstance $SourceRBclass -ComputerName $SourceSMserver -filter 'Status -eq "Runbook.Status.Active"'
Write-Host "$SourceSMserver Runbook Items collected."

## Get the list of active runbook CIs in Target Service Manager
Write-Host "Connecting to $TargetSMserver and identifying the list of active Runbook Items ..." -ForegroundColor Green -BackgroundColor DarkBlue
$TargetRBclass = Get-SCClass -Name $RBItemClass -ComputerName $TargetSMserver
$TargetRBInstances = Get-SCClassInstance $TargetRBclass -ComputerName $TargetSMserver -filter 'Status -eq "Runbook.Status.Active"'
Write-Host "$TargetSMserver Runbook Items collected."

 

These next two sections begin the meat of the script, and also represent the two slowest steps as the script communicates with Service Manager. Here, the script connects to the Source and Target Service Manager environments, searching for any active Runbook Items, then storing the returned instances within arrays. The status filter is included both to decrease the amount of items processed and returned, speeding up both this query and the script’s later functions, as well as ensuring that Runbooks once synced to Service Manager but no longer existing in Orchestrator will not negatively impact the GUID updating.

## Match the runbook GUIDs between environments and update the management packs
Write-Host "Processing runbook information and updating management packs ..." -ForegroundColor Green -BackgroundColor DarkBlue
foreach($SourceRBInstance in $SourceRBInstances)
	{
	Write-Host "Now processing: $SourceRBInstance"
	$OldGUID = [Guid]$SourceRBInstance.Id
	$TargetRBInstance = $TargetRBInstances | where{$_.DisplayName -eq $SourceRBInstance.DisplayName}
	If(($TargetRBInstance).count -eq 1)
		{
		$NewGUID = [Guid]$TargetRBInstance.Id
		foreach($MP in (GCI $UnsealedMPFolder -Filter "*.xml"))
			{
			$FullName = $MP.fullname
			(Get-Content $FullName) | 
			Foreach-Object {$_ -replace $OldGUID, $NewGUID} | 
			Set-Content $FullName -Encoding UTF8
			}
		}
	Elseif(($TargetRBInstance).count -gt 1)
		{
		$RBInstanceCount = ($TargetRBInstance).count
		Write-Host "$RBInstanceCount instances of $SourceRBInstance found in target environment." -ForegroundColor Yellow -BackgroundColor DarkBlue
		Write-Host "Runbooks should each have unique names!  Please update your runbooks, re-sync with both Service Manager environments, and start over." -ForegroundColor Yellow -BackgroundColor DarkBlue
		Write-Host "Script will continue to process other runbooks, however any reference to a runbook with a non-unique name will not be updated." -ForegroundColor Yellow -BackgroundColor DarkBlue
		}
	Else
		{
		Write-Host "SourceRBInstance not found in target environment; skipping ..." -ForegroundColor Yellow -BackgroundColor DarkBlue
		}
	}
Write-Host "Processing complete." -ForegroundColor Green -BackgroundColor DarkBlue

 

The final section performs the actual task, processing the found GUIDs and updating the management packs. This section tends to run quickly, even when processing multiple dozens of management packs at a time.

 

It is composed of a series of Foreach and If/Then commands which, for each Runbook Item in the list generated from the Source Service Manager environment, first stores that Runbook Item’s GUID in a variable, then compares the Runbook Item’s DisplayName property with the list from the Target environment and looks for a match. The DisplayName property is used because that is the only property that is guaranteed to be the same for a given Runbook in both Service Manager environments. The Runbook Folder Path could be used, in theory, if the same Orchestrator environment was synced to both Service Manager environments. However, this is not guaranteed to be the case (and I have worked with some clients wherein it surely hasn’t been), whereas the DisplayName of a single Runbook synced to multiple Service Manager environments will always be the same in all environments. However, as Orchestrator allows multiple Runbooks with the same name, the script must account for times when multiple Runbooks with the same name are found.

 

Therefore, the script counts the number of Runbook Items from the Target environment that match the DisplayName of the Source environment Runbook Item. If multiple Runbook Items are found, the script writes a notification to the host and skips processing of that Runbook, moving on to the next Runbook Item in the list. Similarly, if a match cannot be found because there are no matching Runbooks with the same DisplayName in the Target environment, the script again writes a notification to the host and continues on to the next Runbook Item in the list. When the script finds a single Runbook Item with a matching DisplayName, it stores the matching Runbook Item’s GUID within a new variable, then proceeds to process each management pack (.xml file) in the $UnsealedMPFolder folder, searching for any instances of the Source environment’s Runbook Item’s GUID and replacing it with the Target environment’s Runbook Item’s GUID. This process is repeated for each Runbook Item in the Source environment’s list. Upon completion, the script announces its success and ends.

 

At this point, aside from investigating the Runbooks that were skipped, all management packs have been updated and are ready for import into the Target environment!

 

CLOSING THOUGHTS AND DOWNLOAD

Standard disclaimer: This script is not provided by Microsoft and is thus not supported by Microsoft. ALWAYS test any scripts you find on the internet in your Test or Development environment before using in Production!

 

I have used this script in multiple System Center 2012 R2: Service Manager environments and have not yet experienced any issues. As all PowerShell cmdlets used are also present in the 2012 RTM and 2012 SP1 releases of Service Manager, this script should also work perfectly for those environments (though the $SMInstallPath variable will need to be updated with the appropriate install location).

 

For me, this solution has saved hours upon hours of time and, as part of my Service Manager Migration tool set, has been invaluable during any migration. It has also had the benefit of making management pack migration processes far easier for my clients, enabling them to perform migrations on their own while minimizing room for error.

 

I have additional scripts in my Service Manager Migration tool set which are bound to make their way up here eventually. For now, you can find the link to download the completed UpdateRunbookGUIDs script below. If you have any problems or questions, please post them in the comments below. Thanks for reading!

 

Along with this post, I have also published this script on Technet. Use the following links to view the script on Technet or to download it directly from here:

 

http://gallery.technet.microsoft.com/Service-Manager-Migration-f8f6d196

 

UpdateRunbookGUIDs.zip

Article By Gabriel Taylor
With over 12 years of experience in the IT industry, Gabriel brings a focus on repeatable processes, solution design, and quality execution to Model’s Project Services practice. He believes the true value of technology is how it enables businesses to gain efficiencies, increase productivity, and achieve their goals. He is proud to work with Model’s team of experts to bring those benefits to Model’s clients.

Related Posts