UPDATE 02-01-2014: Fixed some issues in script
When dealing with the cmdlet: Set-SCSMTemplate in SMLets, you might have noticed that if you apply a template with activities, the prefix of the ID’s (e.g. RA300 or MA250) is all missing. And it’s the same issue if done via the SDK or Orchestrator.
One workaround, described by Lee Berg here: http://www.leealanberg.com/blog/2013/03/13/scsm-automated-service-request-smlets-creation-issues-and-work-arrounds/ is to modify the management pack that contains the template, and then insert the prefix like this: MA{0}. This approach works, but can be quite cumbersome as it takes time to do and also “locks” the template so any modification done to the template via the console will reset the id property in the management pack. Which means you have to insert the property again in the management pack.
This script takes care of the problem with no need to modify the management pack. All you need to provide is the workitem guid and the guid or displayname of the template that you want to apply.
If you are using Orchestrator, then my collegue Jakob has made a cool Integration pack that takes care of the problem: https://blog.ctglobalservices.com/jgs/scoscsm-2012-create-objects-with-activities-with-coretech-integration-pack-for-scsm-2012-extension-beta-2/
My script is used when you want to apply a template on an already created workitem, where Jakob’s IP is used for creating new workitems based on a template. The same issue will happen in both scenarios. If people find the script useful I could make another script to create a new workitem based on a template as well.
This is the first version, I’d be happy to get your feedback:
Script:
The highlevel steps in the script is to
- Analyze the template for activities,
- Update the template with the right activity prefixes, and then finally
- Commit the updated template to the workitem.
Nothing is being changed or needs to be changed in the management pack with this method.
# //*************************************************************************** # // Author: Morten Meisler, Coretech A/S. https://blog.ctglobalservices.com/mme # // # // Usage: Script to apply a template with activities on a workitem and correct # // id prefixes on each activity # // # // # // CORETECH A/S History: # // 1.0 27/12/2013 beta version 1. # // 1.1 02-01-2014 Fixed issue with activities containing other objects (like system.reviewer) # //*************************************************************************** Param ([guid]$Id =$(throw "Please provide guid of the workitem"), [string]$TemplateDisplayName, [guid]$TemplateId, [string]$ComputerName = "localhost") #---------------------------------------------------- #Function to get activity id prefix from the activity settings #---------------------------------------------------- function Get-SCSMObjectPrefix { Param ([string]$ClassName =$(throw "Please provide a classname")) switch ($ClassName) { default { #Get prefix from Activity Settings if ($ClassName.StartsWith("System.WorkItem.Activity") -or $ClassName.Equals("Microsoft.SystemCenter.Orchestrator.RunbookAutomationActivity")) { $ActivitySettingsObj = Get-SCSMObject -Class (Get-SCSMClass -Id "5e04a50d-01d1-6fce-7946-15580aa8681d") if ($ClassName.Equals("System.WorkItem.Activity.ReviewActivity")) {$prefix = $ActivitySettingsObj.SystemWorkItemActivityReviewActivityIdPrefix} if ($ClassName.Equals("System.WorkItem.Activity.ManualActivity")) {$prefix = $ActivitySettingsObj.SystemWorkItemActivityManualActivityIdPrefix} if ($ClassName.Equals("System.WorkItem.Activity.ParallelActivity")) {$prefix = $ActivitySettingsObj.SystemWorkItemActivityParallelActivityIdPrefix} if ($ClassName.Equals("System.WorkItem.Activity.SequentialActivity")) {$prefix = $ActivitySettingsObj.SystemWorkItemActivitySequentialActivityIdPrefix} if ($ClassName.Equals("System.WorkItem.Activity.DependentActivity")) {$prefix = $ActivitySettingsObj.SystemWorkItemActivityDependentActivityIdPrefix} if ($ClassName.Equals("Microsoft.SystemCenter.Orchestrator.RunbookAutomationActivity")) {$prefix = $ActivitySettingsObj.MicrosoftSystemCenterOrchestratorRunbookAutomationActivityBaseIdPrefix } } else {throw "Class Name $ClassName is not supported"} } } return $prefix } #---------------------------------------------------- #Function to set id prefix to activities in template #---------------------------------------------------- function Update-SCSMPropertyCollection { Param ([Microsoft.EnterpriseManagement.Configuration.ManagementPackObjectTemplateObject]$Object =$(throw "Please provide a valid template object")) #Regex - Find class from template object property between ! and '] $pattern = '(?<=!)[^!]+?(?=''\])' if (($Object.Path) -match $pattern -and ($Matches[0].StartsWith("System.WorkItem.Activity") -or $Matches[0].StartsWith("Microsoft.SystemCenter.Orchestrator"))) { #Set prefix from activity class $prefix = Get-SCSMObjectPrefix -ClassName $Matches[0] #Create template property object $propClass = [Microsoft.EnterpriseManagement.Configuration.ManagementPackObjectTemplateProperty] $propObject = New-Object $propClass #Add new item to property object $propObject.Path = "`$Context/Property[Type='$alias!System.WorkItem']/Id$" $propObject.MixedValue = "$prefix{0}" #Add property to template $Object.PropertyCollection.Add($propObject) #recursively update activities in activities if ($Object.ObjectCollection.Count -ne 0) { foreach ($obj in $Object.ObjectCollection) { Update-SCSMPropertyCollection -Object $obj } } } } #---------------------------------------------------- #Function to apply template after it has been updated #---------------------------------------------------- function Apply-SCSMTemplate { Param ([Microsoft.EnterpriseManagement.Common.EnterpriseManagementObjectProjection]$Projection =$(throw "Please provide a valid projection object"), [Microsoft.EnterpriseManagement.Configuration.ManagementPackObjectTemplate]$Template = $(throw 'Please provide an template object, ex. -template template')) #Get alias from system.workitem.library managementpack to set id property $templateMP = $Template.GetManagementPack() $alias = $templateMP.References.GetAlias((Get-SCSMManagementPack system.workitem.library)) #Update Activities in template foreach ($TemplateObject in $Template.ObjectCollection) { Update-SCSMPropertyCollection -Object $TemplateObject } #Apply update template Set-SCSMObjectTemplate -Projection $Projection -Template $Template -ErrorAction Stop Write-Host "Successfully applied template:`n"$template.DisplayName "`nTo:`n"$Projection.Object } #-------------------------------- #INITIALIZE #-------------------------------- $SMDefaultComputer = $ComputerName #Load SMlets module if (!(Get-Module smlets)) {Import-Module smlets -force -ErrorAction stop} #Get object from guid $emo = get-scsmobject -id $id #determine projection according to workitem type switch ($emo.GetLeastDerivedNonAbstractClass().Name) { "System.workitem.Incident" {$projName = "System.WorkItem.Incident.ProjectionType" } "System.workitem.ServiceRequest" {$projName = "System.WorkItem.ServiceRequestProjection"} "System.workitem.ChangeRequest" {$projName = "System.WorkItem.ChangeRequestProjection"} "System.workitem.Problem" {$projName = "System.WorkItem.Problem.ProjectionType"} "System.workitem.ReleaseRecord" {$projName = "System.WorkItem.ReleaseRecordProjection"} default {throw "$emo is not a supported workitem type"} } #Get object projection $emoID = $emo.id $WIproj = Get-SCSMObjectProjection -ProjectionName $projName -Filter "Id -eq $emoID" #Get template from displayname or id if ($TemplateDisplayName) { $template = Get-SCSMObjectTemplate -DisplayName $TemplateDisplayName } elseif ($templateId) { $template = Get-SCSMObjectTemplate -id $TemplateId } else { throw "Please provide either a template id or a template displayname to apply" } #Execute apply-template function if id and 1 template exists if (@($template).count -eq 1) { if ($WIProj) { Apply-SCSMTemplate -Projection $WIproj -Template $template } else {throw "Id $Id cannot be found";} } else{throw "Template cannot be found or there was more than one result"}
Usage:
Import-Module SMlets
$SR = Get-SCSMObject -Class (Get-SCSMClass -Name system.workitem.servicerequest$) -Filter "Id -eq SR35945" .\Set-SCSMTemplatewithActivities.ps1 -Id $SR.get_id() -TemplateId af196f87 -cffe-eca1-cad5-51e8c4268ebd #OR .\Set-SCSMTemplatewithActivities.ps1 -Id $SR.get_id() -TemplateDisplayName "Coretech - SR - Template with activities"
Download:
http://gallery.technet.microsoft.com/Set-SCSMTemplateWithActivit-7343d96a
Leave a comment or write me an email for feedback
Cheers
Morten
Thanks so much for this script! My struggle however, is that it complains that Class Name System.Reviewer is not supported. Why do you think this could be happening?
PS C:Userssvc_scsmDesktop> $crObject.get_id()
Guid
—-
9693aaea-96e9-0b2f-407b-6b9299ef40f7
PS C:Userssvc_scsmDesktop> $template.get_id()
Guid
—-
fc7b4076-77cf-ea88-6204-13dd064ec2ee
PS C:Userssvc_scsmDesktop> C:exportSCSMSet-SCSMTemplatewithActivities.ps1 -Id $crObject.get_id() -TemplateId $template.get_id()
Class Name System.Reviewer is not supported
At C:exportSCSMSet-SCSMTemplatewithActivities.ps1:41 char:19
+ else {throw “Class Name $ClassName is not supported”}
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OperationStopped: (Class Name Syst…s not supported:String) [], RuntimeException
+ FullyQualifiedErrorId : Class Name System.Reviewer is not supported
Thank you!!
Looks like a bug in script where system.reviewer is included in the regex, ill look into it soon when I get to a PC 🙂
Ok, the issue has been fixed now so it only looks at objects of Activity and runbook class type.
Hallo, ich würde gerne VILLAGE spielen, das Keeinrspnel des Jahres 2012. Falls jemand Lust hat, bitte einfach melden. Ich würde das Spiel mitbringen, habe die Regeln aber noch nicht studiert.
If your articles are always this helpful, “I’ll be back.”
Usually I do not read post on blogs, but I would like to say that this write-up very forced me to check out and do so! Your writing style has been surprised me. Thanks, very great article.
[…] Coretech Morten Meisler – Set-SCSMTemplateWithActivities powershell script http://blog.coretech.dk/mme/set-scsmtemplatewithactivities-powershell-script/ […]
[…] – Coretech Morten Meisler – Set-SCSMTemplateWithActivities powershell script http://blog.coretech.dk/mme/set-scsmtemplatewithactivities-powershell-script/ […]
All My Activities are in state pending. Activities are not started.
Only if I afterwards set the first activity to in progress.
Do you have the same behaviour ?
Done some tests now. I suspect that you create your Workitem through powershell right ? That’s an issue related to creating workitem through sdk. It’s described here: http://social.technet.microsoft.com/Forums/systemcenter/en-US/988a2d4e-6d54-4607-9082-509c6b5cc4d0/service-request-wtemplate-created-with-smlets-does-not-start-activities?forum=systemcenterservicemanager
To fix this, I would rather make another version of the script where you are able to create workitems based on a template. Today we have the New-SCSMObjectProjection -projection $yourservicerequestprojection -template $yourtemplate. But this creates the exact same issue with missing prefixes + activities not starting. If you are using Orchestrator, I recommend using my collegues integration pack that deals with this issue.
So stay tuned ! 🙂
Hi Frank,
This was an old issue that should have been fixed, it works fine in mine. What version are you using? Luckily the fix is easy enough, just set the first activity in the template to In Progress.
I will see that it gets implemented in the script as soon as I got time.
Morten! Thank you very much.
With a little bit of finessing I was able to add bits of this to my script and fix the missing “RB/RA/MA”! Now when I spawn my new Service requests from PowerShell – they look “normal”.
thank you again!
Jeremy
Hi Jeremy,
That’s good to hear 🙂
Feel free to send me updates to the code if you think it can be improved in any way.
Hey, I’ve come up with a solution that quickly checks a whole management pack for missing prefixes and fixes them. Here’s the source:
Repair-ManagementPackExport managementpackName.xml
.INPUTS
System.String
.NOTES
If this file does not have strings requiring repair, then it will not be modified.
.VERSION
1.1
#>
function Repair-ManagementPackExport
{
param ( [parameter(mandatory=$true)] [System.String] $xmlFileFullPath )
# Select all nodes which contain a child path attribute that has a description, but does not contain a child path having an ID
# note, special use of @ char, the last line with the “@ character must have no white space before it or the multiline string won’t be recognized
[string] $xPathExpression = @”
//*[
Property[contains(@Path, “$Context/Property[Type=’CustomSystem_WorkItem_Library!System.WorkItem’]/Description$”)] and
not (Property[contains(@Path, “$Context/Property[Type=’CustomSystem_WorkItem_Library!System.WorkItem’]/Id$”)])
]
“@
$ErrorActionPreference = “Stop”
try
{
if (-not (Test-Path $XmlFileFullPath))
{
$msg = “The file $XmlFileFullPath does not exist.”
throw [System.IO.FileNotFoundException] $msg
}
$xmlFileFullPath = Resolve-Path -Path $xmlFileFullPath
[Xml.XmlDocument] $xdoc = Get-Content $XmlFileFullPath
[Xml.XmlNodeList] $nodes = $xdoc.SelectNodes($xPathExpression)
if ($nodes -eq $null)
{
Write-Host “No errors detected”
return
}
[int] $nodeCount = $nodes.Count
Write-Host “Starting Operation – Adding $nodeCount missing ID tags in XML”
[Xml.XmlElement] $newNode = $null
for ([int]$i=0; $i -lt $nodeCount; ++$i)
{
$newNode = $xdoc.CreateElement(‘Property’)
$newNode.SetAttribute(‘Path’, ‘$Context/Property[Type=”CustomSystem_WorkItem_Library!System.WorkItem”]/Id$’)
$newNode.InnerText = ”
[Xml.XmlElement] $elementToInsertAfter = $null
# check first to see if the activity type is defined at the higher level object. If it is, use this path since the children propty objects will only
# contain generic system activity paths
if ($nodes[$i].Path -ne $null)
{
$newNode.InnerText = ReturnIDType $nodes[$i].Path
}
foreach ($propElement in $nodes[$i].Property)
{
# only process if we haven’t found the element to insert after
if ($elementToInsertAfter -eq $null)
{
# find the correct location to insert the new node (insert right below the ID node
# this is being done because there are times when the objects that require this node insertion have nested objects. If the ID tag for the higher level object gets
# inserted below the child object, it’ll be considered invalid, so just safer to place around title
if ($propElement.Path.Contains(“`$Context/Property[Type=’CustomSystem_WorkItem_Library!System.WorkItem’]/Description$”))
{
$elementToInsertAfter = $propElement
}
}
# only process if we haven’t yet found the activity type
if ($newNode.InnerText -eq ”)
{
$newNode.InnerText = ReturnIDType $propElement.Path
}
# we found all the info we need, break out early
if ( ($newNode.InnerText -ne ”) -and ($elementToInsertAfter -ne $null) )
{
break
}
}
# the innerText is empty if we have a template without activities, in this case we do not need the ID
if ($newNode.InnerText -ne ”)
{
if ($elementToInsertAfter -eq $null)
{
throw “We have an issue with the XML formation. Expected Description tag is missing. Please review the Management pack file being submitted.”
}
$tmp = $nodes[$i].InsertAfter($newNode, $elementToInsertAfter);
}
}
# we don’t have a document encoding element, add one so that we don’t lose character encoding
if ($xdoc.DocumentElement.OwnerDocument.xml -eq $null)
{
[Xml.XmlElement] $root = $xdoc.DocumentElement
[Xml.XmlDeclaration] $xmldecl = $xdoc.CreateXmlDeclaration(“1.0″,”UTF-16″,””)
$tmp = $xdoc.InsertBefore($xmldecl, $root)
}
$xdoc.Save($XmlFileFullPath)
Write-Host “Completed Operation – Added $nodeCount missing ID tags in XML.”
}
catch
{
# Throwing an exception in this manner will actually bubble up to the Orchestrator runbook activity and give an error condition.
Throw
}
}
Export-ModuleMember -Function Repair-ManagementPackExport
#region Private functions
function ReturnIDType
{
param ( [parameter(mandatory=$true)] [System.String] $pathText )
# only load this settings object once (create a script level variable, acting like a static for this session)
if ($script:activitySettingsObj -eq $null)
{
$script:activitySettingsObj = Get-SCSMObject -Class (Get-SCSMClass -Id “5e04a50d-01d1-6fce-7946-15580aa8681d”)
}
[string] $retVal = ”
if ($pathText.Contains(“Microsoft.SystemCenter.Orchestrator.RunbookAutomationActivity”))
{
$retVal = $script:activitySettingsObj.MicrosoftSystemCenterOrchestratorRunbookAutomationActivityBaseIdPrefix
}
elseif ($pathText.Contains(“System.WorkItem.Activity.ManualActivity”))
{
$retVal = $script:activitySettingsObj.SystemWorkItemActivityManualActivityIdPrefix
}
elseif ($pathText.Contains(“System.WorkItem.Activity.ReviewActivity”))
{
$retVal = $script:activitySettingsObj.SystemWorkItemActivityReviewActivityIdPrefix
}
elseif ($pathText.Contains(“System.WorkItem.Activity.ParallelActivity”))
{
$retVal = $script:activitySettingsObj.SystemWorkItemActivityParallelActivityIdPrefix
}
elseif ($pathText.Contains(“System.WorkItem.Activity.SequentialActivity”))
{
$retVal = $script:activitySettingsObj.SystemWorkItemActivitySequentialActivityIdPrefix
}
elseif ($pathText.Contains(“System.WorkItem.Activity.DependentActivity”))
{
$retVal = $script:activitySettingsObj.SystemWorkItemActivityDependentActivityIdPrefix
}
if ($retval -ne ”) { $retVal += “{0}” }
return $retVal
}
#endregion
You sweet Beautiful Ginger Man!
This is simply brilliant! I don’t know how I didn’t find this post last year when we first had the issue, but its really going to save our sanity, as we implement change and release as we are automating the creation of as much of this as we can!
from your Ginger UK Brother!
Ed
Thank you very much for the author to solve my problem.I met the question is, can’t find the first activity in the template and its status is set to start.On the basis of the author code changes:
Note: in the template avtivities number order must, from top to bottom is growing up.
1:Changes in the Apply-SCSMTemplate function.
#Update Activities in template
[int]$count = 0
foreach ($TemplateObject in $Template.ObjectCollection)
{
Update-SCSMPropertyCollection -Object $TemplateObject
$count++;
}
2:Changes in the Update-SCSMPropertyCollection function.
#Regex – Find class from template object property between ! and ‘]
$pattern = ‘(?<=!)[^!]+?(?=''])'
if (($Object.Path) -match $pattern -and ($Matches[0].StartsWith("System.WorkItem.Activity") -or $Matches[0].StartsWith("Microsoft.SystemCenter.Orchestrator")))
{
#Handle the first Activity
if ($count -eq 0)
{
#Create template property object
$propClass = [Microsoft.EnterpriseManagement.Configuration.ManagementPackObjectTemplateProperty]
$propObject = New-Object $propClass
#Add new item to property object
$propObject.Path = "`$Context/Property[Type='$aliasActivity!System.WorkItem.Activity']/Status$"
$propObject.MixedValue = "11fc3cef-15e5-bca4-dee0-9c1155ec8d83"
#Add property to template
$Object.PropertyCollection.Add($propObject)
}
#Set prefix from activity class
$prefix = Get-SCSMObjectPrefix -ClassName $Matches[0]
…
…
}
… [Trackback]
[…] Find More here|Find More|Read More Informations here|There you can find 90150 additional Informations|Infos on that Topic: blog.ctglobalservices.com/service-manager-scsm/mme/set-scsmtemplatewithactivities-powershell-script/ […]
This script deservingly merits full praise, well done in figuring out the required objects and how to apply them to automate a request offering. For someone that has just stepped aboard the SCSM platform a fortnight ago, I did not know where to start. This script has saved my skin. Thank you,
Can´t compile the code in Service Manager Authoring Tool, despite running on Windows Powershell ISE. The problem seems to be in this line: $propObject.Path = “`$Context/Property[Type=’$alias!System.WorkItem’]/Id$”
I call this script at the end of a powershell script that creates a service request.
The first time it works.
But if a service request aleady exists where the script Set-SCSMTemplatewithActivities.ps1 has been applied onto,
I get the error message:
Set-SCSMObjectTemplate : Ungültige Formatspezifikation: “s} already has lifetime dependency defined by relationship id = (%s).”.
In Set-SCSMTemplatewithActivities.ps1:100 Zeichen:5
+ Set-SCSMObjectTemplate -Projection $Projection -Template $Templat …
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (Template.03f311…32d32af02cc80c2:ManagementPackObjectTemplate) [Set-SCSMObjectTemplate], UnknownDatabaseException
+ FullyQualifiedErrorId : ApplyTemplate,SMLets.SetSCSMObjectTemplateCommand
Anyone an idea what I am doing wrong?