Set-SCSMTemplateWithActivities powershell script

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: http://blog.coretech.dk/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

  1. Analyze the template for activities,
  2. Update the template with the right activity prefixes, and then finally
  3. 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. http://blog.coretech.dk/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


Comments (10):

  1. John Croson says:

    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:\Users\svc_scsm\Desktop> $crObject.get_id()

    Guid
    —-
    9693aaea-96e9-0b2f-407b-6b9299ef40f7

    PS C:\Users\svc_scsm\Desktop> $template.get_id()

    Guid
    —-
    fc7b4076-77cf-ea88-6204-13dd064ec2ee

    PS C:\Users\svc_scsm\Desktop> C:\export\SCSM\Set-SCSMTemplatewithActivities.ps1 -Id $crObject.get_id() -TemplateId $template.get_id()
    Class Name System.Reviewer is not supported
    At C:\export\SCSM\Set-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!!

    • Morten Meisler Morten says:

      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 :-)

    • Morten Meisler Morten says:

      Ok, the issue has been fixed now so it only looks at objects of Activity and runbook class type.

  2. Frank says:

    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 ?

  3. Morten Meisler Morten says:

    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.

  4. Jeremy Castleberry says:

    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

    • Morten Meisler Morten says:

      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.

Leave a Reply

(required)