Updating a Change Request when Activities are Updated Using PowerShell in a Workflow
Published Feb 15 2019 05:14 AM 522 Views
First published on TECHNET on Mar 15, 2011

Andreas Rynes, a Microsoft Consulting Services consultant, has come up with a clever solution to a problem.  The problem is how to surface up the contained activity details to the change request level.  This makes it easier to format/send notifications and such things.  He wrote a guest blog post about his solution which uses the SMLets PowerShell module (a CodePlex solution) to get the activity and update the parent change request description.


Thanks for figuring this out and sharing it Andreas!


====================================================


During my work with Service Manager I’ve got a requirement that data from custom activities should be update to the main form of the change request. So in the case of a change request template that has multiple activities and all are having some custom fields, every time a single activity from a change request is updated with some data, the complete list of activity data (not just the one that got updated, also all the other activities from the same change request) should be written to the description field of the parent change request or should be updated if it already exists. Manually entered descriptions from users should be kept and not overwritten by that solution.


The overall idea is to implement that in a generic way, so that even custom activity classes that are implemented in the future are working with that solution as well.


This is how it might look, after you’ve implemented my solution:



Seems not possible with the out of the box features of SCSM, but it is possible with a custom workflow and Windows Powershell. Thanks to Jim Truher, there is a Service Manager Cmdlet for Powershell and thanks to the Product itself there is an easy way to create a custom workflow with a Powershell script.


1. Custom Activity

So I don’t tell you in this blog post, how to create a custom activity, but there is an excellent post about that from Travis Wright and Jim Pitts: http://blogs.technet.com/b/servicemanager/archive/2010/11/16/how-to-automate-vm-provisioning-in...


So after you’ve created a similar sample of a custom activity that has some custom attributes and a custom form to enter the data and you’ve imported that to SCSM, you can create an activity template and a change request template that has this new custom activity defined. (You’ll find both templates in the MP attached to this blog post)


2. Custom Workflow in the SCSM Authoring Tool

Now it’s time to create the custom workflow, go to Service Manager Authoring Tool, create a new MP and a new workflow, give it a name and enter some description in the wizard.


On the next page you’ve been asked to provide the trigger (time based or database based). This time we need to specify “Run only when a database object meets specified conditions” as we like to trigger that workflow when an activity is updated/changed.


On the next page click the Browse button and choose Activity, that means that this workflow triggers whenever an class that inherited from Activity is updated (so in our case we’ve a custom activity that inherits from ManualActivity, which inherits from the base class Activity).


Choose “When an object of the selected class is updated” in the Change Event box. You could possible define additional criteria, but this time we don’t do that (as this should be a generic solution). Then you’re done, click on Next, Create and Close button.



After that you’ll find the new workflow completely empty in the main pane in the middle and you’re able to add a new shape to the area saying “Drop activities to create a Sequential Workflow”.


Drag and drop a “Windows Powershell Script” shape to the workflow and change the name to something that makes sense for your sample.



This is the result at that stage:



3. Powershell Script and Parameter

As long as you see the red exclamation mark in the corner of a shape, there is some information missing. In this case the script body and its parameter are not defined yet, so that makes the Powershell worthless for now, so we are going to define those values:



The script body:


set-executionpolicy -executionPolicy ByPass


$a = (get-module|%{$_.name}) -join " "


if(!$a.Contains("SMLets")){Import-Module SMLets -ErrorVariable err -Force}


This part defines the execution policy for the Powershell session. For that sample I’ve used ByPass, which allows to run everything, which might not be perfect and you should consider digital signing the cmdlets you are using. Then I’m going to get all loaded modules (get-module), check if the SMLets module is already loaded (Contains) and load it if not done before. The –ErrorVariable is used for troubleshooting only, so if you’re not going to use the variable you can skip it. I’ll describe troubleshooting techniques for that in an separate post later.


$indexend = (Get-SCSMRelationshipObject -Target (get-SCSMClass System.WorkItem.Activity$)|?{$_.TargetObject -match $activity_id}).SourceObject.ToString().IndexOf(':');


$cr_id = (Get-SCSMRelationshipObject -Target (get-SCSMClass System.WorkItem.Activity$)|?{$_.TargetObject -match $activity_id}).SourceObject.ToString().Substring(0,$indexend);


Then I’m using the SMCmdlet for the first time to get the parent CR from the activity. The activity id is passed into the script (we’ll define that later!) and with Get-SCSMRelationshipObject I’ll get the parent change request of the activity parameter.


$activities = Get-SCSMRelatedObject -SMObject (get-scsmobject (get-scsmclass System.workitem.ChangeRequest$)|?{$_.Id -eq $cr_id}) -Relationship (Get-SCSMRelationshipClass System.WorkItemContainsActivity)


$ActivityDict = @{}


$countKeys = 0



foreach($activity in $activities)


{


foreach($val in $activity.Values)


{


if($val.Type -match "^z.*")


{


$countKeys++


$ActivityDict[$activity.id+"@"+$val.Type] = $val.Value


}


}


}


This parts grabs all the related activities from the parent change request using the SCSMRelatedObject and the SCSMRelationshipClass “WorkItemContainsActivity”. For each activity I’m then look up for values from fields that are name with a starting z and adding those into a dictionary and count them (for later use).


$description_start = "-------------------------------------------------" + "`n" + "-- Activity Data Begin (Do not change!) --" + "`n" + "-------------------------------------------------" + "`n"


$description_ende = "------------------------------------------------" + "`n" + "-- Activity Data End (Do not change!) --" + "`n" + "------------------------------------------------" + "`n"



$desc_beginindex= (Get-SCSMObject (get-SCSMClass System.WorkItem.ChangeRequest$) -Filter "Id -eq $cr_id").Description.ToString().IndexOf("Activity Data Begin");


$desc_endeindex= (Get-SCSMObject (get-SCSMClass System.WorkItem.ChangeRequest$) -Filter "Id -eq $cr_id").Description.ToString().IndexOf("Activity Data End");



if($desc_beginindex -eq -1) {$desc_beginindex = 0 } else {$desc_beginindex -= 53}


if($desc_endeindex -eq -1) {$desc_endeindex = 0} else {$desc_endeindex += 87}


So then it’s the hard part of the solution J Building the data string in a nice and readable way, which is also easy to recognize to make it updateable. So with the method IndexOf we look for an existing activity data block. If there is none we set the begin and end index to 0 otherwise we set it to the beginning and end of the data block.


$description = (Get-SCSMObject (get-SCSMClass System.WorkItem.ChangeRequest$) -Filter "Id -eq $cr_id").Description.ToString()



$descriptionnew = $description_start



foreach($a in $ActivityDict.Keys)


{


$descriptionnew += $a.Substring(0,$a.IndexOf('@')) + " / " + $a.Substring($a.IndexOf('@')+1,$a.Length-$a.IndexOf('@')-1) + ": " + $ActivityDict[$a].ToString() + "`n"


}


$descriptionnew += $description_ende



$descriptionnew += $description.Substring(0,$desc_beginindex) + $description.Substring($desc_endeindex,$description.Length-$desc_endeindex)



Now we grab description that is already in the change request with Get-SCSMObject and filter the output by the change request ID. The goal is to keep that data in the description field and not to overwrite it with the new values. Then we iterate the collection and build up the new description, the first part is the activity data then we add the existing data (except the activity data that was already there)



if($countKeys -gt 0){


Set-SCSMObject -SMObject (Get-SCSMObject (get-SCSMClass System.WorkItem.ChangeRequest$) -Filter "Id -eq $cr_id") -Property 'Description' -Value $descriptionnew


}


remove-module -name SMLets –force


The count of the custom fields, where the name of the field starts with z defines if the new description is written to the change request or not. If there are no custom fields, nothing is changed in the change request. So we are using the $count variable and then use Set-SCSMObject to set the description field. At the end we remove the SMLets module, so that we can Import it again the next time (If you don’t remove it, it might be a problem importing the module again)



Now it’s time to define the only input parameter for the script. Click on the “ Script Properties ” tab and enter activity_id in the name field (don’t use the $ here, just in the script you need to reference to $activity_id)



In the value choose a class property and choose the ID of the activity class. This is the only input parameter that we need for that solution.



4. Deployment

Now you’re done with that and save the solution in the Authoring Tool. After that a dll is generated in the Authoring Tool location. This dll has the same name as your MP and must be copied to the Service Manager installation location and the Management Pack needs to be imported in the SCSM console as well.


The last thing that needs to be done is the download and installation of the Service Manager Cmdlet from that location: http://smlets.codeplex.com/


Please be aware of unblock the SMLets archive download before installing it (see documentation at http://smlets.codeplex.com/documentation )


Copy the content as described to the powershell default location c:\windows\system32\WindowsPowerShell\v1.0\Modules\SMLets


In the attachment you’ll find the Management Pack that contains a custom activity, a custom form for that activity and the templates for the activity and change request and the custom workflow of course.


VirtualMaschineManualActivity.xml

Version history
Last update:
‎Mar 11 2019 08:40 AM
Updated by: