Home General Discussion

Dynamically pulling the next RA to add approvers using PS Activity

Bryan_LalorBryan_Lalor Customer IT Monkey ✭
edited September 2023 in General Discussion

I'm working to add a PS activity that assigns reviewers to the next RA, I found Adams code on his PS blog series, but it is missing how to actually point it to the correct RA. Below is the code i'm referencing. He hard codes his name and the RA at the bottom. I have been unsuccessful at finding the RA ID via Get-SCSMRelationshipClass.


function Add-SCSMReviewer ($ReviewActivityID, $SCSMUser, $Computername)
{
    #Get the classes we need to work with
    $raClass = Get-SCSMClass -name "System.WorkItem.Activity.ReviewActivity$" -ComputerName $Computername
    $reviewerClass = Get-SCSMClass -name "System.Reviewer$" -ComputerName $Computername
    $raHasReviewerRelClass = Get-SCSMRelationshipClass "System.ReviewActivityHasReviewer$" -ComputerName $Computername
    $raReviewerIsUserRelClass = Get-SCSMRelationshipClass "System.ReviewerIsUser$" -ComputerName $Computername
    #Get the Review Activity to update
    $ra = Get-SCSMObject -class $raClass -filter "Name -eq '$ReviewActivityID'" -ComputerName $Computername
    #Loop through the incoming array/list of users
    foreach ($user in $SCSMUser)
    {
        #Define an empty Reviewer object and their voting options in memory only by declaring the "-NoCommit" switch
        $reviewer = New-SCSMObject -Class $reviewerClass -PropertyHashtable @{"ReviewerID" = "{0}"; "Veto" = $false; "MustVote" = $false} -ComputerName $Computername -NoCommit
        #Relate the empty Reviewer to the Review Activity in memory by declaring the "-NoCommit" switch
        $RAhasReviewer = New-SCSMRelationshipObject -Source $ra -Relationship $raHasReviewerRelClass -Target $reviewer -ComputerName $Computername -NoCommit
        #Relate the empty Reviewer object to a User in memory by declaring the "-NoCommit" switch
        $reviewerHasUser = New-SCSMRelationshipObject -Source $reviewer -Relationship $raReviewerIsUserRelClass -Target $user -ComputerName $Computername -NoCommit
        #With everything defined in memory, finally commit/save all of those items in one go to Service Manager
        $RAhasReviewer.Commit()
        $reviewerHasUser.Commit()
    }

}
$userClass = Get-SCSMClass -name "System.Domain.User$"
$user = Get-SCSMObject -filter "Username -eq 'Adam'"
Add-SCSMReviewer -ReviewActivityID "RA957402" -SCSMUser $user

Best Answers

  • Simon_ZeinhoferSimon_Zeinhofer Customer Advanced IT Monkey ✭✭✭
    edited September 2023 Answer ✓

    @Bryan_Lalor "Get-scsmrelationshipobject" always returns back the relationship object itself. So to target at the source or target you have to write it this way:

    $containsActivity = Get-scsmrelationshipclass -Name 'System.WorkItemContainsActivity$'
    $ra = (Get-scsmrelationshipobject -bysource $SRequest -filter "RelationshipId -eq '$($containsActivity.Id)'" | ? {$_.TargetObject.ClassName -eq 'System.WorkItem.Activity.ReviewActivity'}).TargetObject
    
    


    There are some "problems" with that code though:

    1. If you have more than one RA in that SR template, you would receive an array of RAs, and therefor you would have to iterate through all of them, to find the correct one, else your code to add a reviewer object would fail
    2. A targetobject object is different from receiving the object itself. E.g. if you get the RA directly, you can access all the fields, when using a targetobject, only certain fields, like ID and Name are accessible directly, other values have to be received in a more complicated way. But you can use this targetobject if you want to change a value in it or use it to create a relationship.

    So if you really want to access the Review Activity directly, there is a much better way to do so:

    At first you have to receive the Child ID of the Activity. This ID is unique and can only be used once in all templates. So if your RA has Child ID 25, no other Activity in any template can have that.

    To receive the child ID, you can either

    1. create a dummy SR by that template (after creation skip all activities in it, so nothing can happen, and then save the SR)

    To receive the child ID you can copy the Name of the RA and open it in the SCSM console:


    Inside the activity go to the History tab and collapse the first entry


    There you find the Child Id


    or the much easier way:

    You use this script to receive all Child Ids from a template:

    #$scsm is your SCSM server name
    #$templateName is the name of your SR template
    $scsm = 'SCSM SERVER'
    
    $activityTemplates = [System.Collections.ArrayList]::new()
    $activityTemplates.Add("SRName,ActivityName,ChildID")
    $templateName = "YOUR TEMPLATE NAME"
    
    Function Get-Activities
    {
    
      Param(
         
        $Template,
        $ObjectCollection
    
      )
    
    
      $aHashtable = @{}
      $pCollection = $ObjectCollection.PropertyCollection
      foreach($pr in $pCollection)
      {
    
        $aHashtable.Add($pr.Path,$pr.MixedValue)
    
      }
    
      $aString = $Template.DisplayName + "," + $aHashTable.'$Context/Property[Type=''CustomSystem_WorkItem_Library!System.WorkItem'']/Title$' + "," + $aHashTable.'$Context/Property[Type=''CustomSystem_WorkItem_Activity_Library!System.WorkItem.Activity'']/ChildId$'
      $activityTemplates.Add($aString)
    
    }
    
    Function Get-SAsAndPAs{
    
      Param(
         
        $Template,
        $ObjectCollection
    
      )
    
      $objColl = $ObjectCollection.ObjectCollection
      foreach($obj in $objColl)
      {
    
        if((-not $obj.Path.Contains("TypeConstraint='CustomSystem_WorkItem_Activity_Library!System.WorkItem.Activity.SequentialActivity'")) -and (-not $obj.Path.Contains("TypeConstraint='CustomSystem_WorkItem_Activity_Library!System.WorkItem.Activity.ParallelActivity'")))
        {
    
          Get-Activities -Template $Template -ObjectCollection $obj
    
        }
         
        elseif(($obj.Path.Contains("TypeConstraint='CustomSystem_WorkItem_Activity_Library!System.WorkItem.Activity.SequentialActivity'")) -or ($obj.Path.Contains("TypeConstraint='CustomSystem_WorkItem_Activity_Library!System.WorkItem.Activity.ParallelActivity'")))
        {
    
          Get-SAsAndPAs -Template $Template -ObjectCollection $obj
    
        }
    
      }
    
    
    }
    
    
    
    
    
    
    $temp = Get-scsmobjecttemplate -ComputerName $scsm | ? { $_.DisplayName -like "*$templateName*" }
    
    
      foreach($o in $temp.ObjectCollection)
      {
    
      if((-not $o.Path.Contains("TypeConstraint='CustomSystem_WorkItem_Activity_Library!System.WorkItem.Activity.SequentialActivity'")) -and (-not $o.Path.Contains("TypeConstraint='CustomSystem_WorkItem_Activity_Library!System.WorkItem.Activity.ParallelActivity'")))
      {
         
          Get-Activities -Template $temp -ObjectCollection $o
    
      }
      elseif(($o.Path.Contains("TypeConstraint='CustomSystem_WorkItem_Activity_Library!System.WorkItem.Activity.SequentialActivity'")) -or ($o.Path.Contains("TypeConstraint='CustomSystem_WorkItem_Activity_Library!System.WorkItem.Activity.ParallelActivity'")))
      {
           
           
          Get-SAsAndPAs -Template $temp -ObjectCollection $o
         
      }
         
      }
    

       

       just type in $activityTemplates after it ran through and you will receive a list of all Activities and their Child Ids inside that template.

    If you have the child ID you can use the function "Get-scsmrelatedobject" to receive the RA you need. This function really returns the Activity object and not a targetobject like before:

    $containsActivity = Get-scsmrelationshipclass -Name 'System.WorkItemContainsActivity$'
    $childID = 'YOUR CHILDID'
    $RA = Get-scsmrelatedobject -smobject $SRequest -relationship $containsActivity -Depth Recursive | ? {$_.ChildId -eq $childId}
    
    

    That's it, then you can use the $RA object to add the reviewer object and so on.

    If you have any questions, just ask ;)

  • Simon_ZeinhoferSimon_Zeinhofer Customer Advanced IT Monkey ✭✭✭
    edited September 2023 Answer ✓

    @Bryan_Lalor

    For the RA part, normally if you receive more than one, the order should always be the same. But again, I would always prefer using the Child ID for that - We use the Child ID for adding reviewers dynamically, in Subscriptions and so on, and for us it works nearly 100 % of the time.

    As far as the User objects go: You could use the username (Samaccountname in AD) as domain/username is the primary key in the SCSM database, so it can only exist once - So you should receive exactly the user you need, even if there is one or more users with the same displayname.

    So if you say

    $SCSMPRarray = get-ADGroupMember -Identity "SCSM_PeerReviewers"
    $userClass = Get-scsmclass -name 'Microsoft.AD.User$'
    $scsmUser = @() 
    foreach($a in $SCSMPRarray)
    {
        $scsmUser += (Get-scsmobject -class $userClass -filter "Username -eq '$($a.Samaccountname)'")
    }
    

    you first receive all AD users, then create an empty array and then, with an iteration, you fill the array with the corresponding SCSM User. Now in your Add-SCSMReviewer function you can directly iterate through the array with the userobjects in it and add them to the reviewer objects - Or, to limit the number of iterations, you could rewrite the function. I did a little change and the function looks like this now (I hope @Adam_Dzyacky is not mad at me for doing so 😉):

    <#parameter $requestID is the ID SR* of your service request
    parameter $childID is the child ID of the RA you need
    parameter $groupname is the samaccountname of your AD group. So if you need another group,
    you only need to change the name in the function call
    #>
     
    function Add-SCSMReviewer ($requestID,$childID, $groupName)
    
    {
    
      #Server Name
      $SMDefaultComputer = "SCSMSERVER"
    
      $userClass = Get-scsmclass -name 'Microsoft.AD.User$'
      $srClass = Get-SCSMClass -name "System.WorkItem.ServiceRequest$"
      $reviewerClass = Get-SCSMClass -name "System.Reviewer$"
    
      $containsActivity = Get-scsmrelationshipclass -Name 'System.WorkItemContainsActivity$'
      $raHasReviewerRelClass = Get-SCSMRelationshipClass "System.ReviewActivityHasReviewer$"
      $raReviewerIsUserRelClass = Get-SCSMRelationshipClass "System.ReviewerIsUser$"
     
      $Srequest = Get-SCSMObject -class $srClass -filter "Name -eq '$($requestID)'"
      $ReviewActivity = Get-scsmrelatedobject -smobject $SRequest -relationship $containsActivity -Depth Recursive | ? {$_.ChildId -eq $childId}
      $SCSMPRarray = get-ADGroupMember -Identity $groupName
    
      foreach ($user in $SCSMPRarray)
    
      {
    
        $UserOBj = Get-SCSMObject -Class $userClass -filter "Username -eq '$($user.Samaccountname)'"
        
        if($UserOBj)
        {
    
            #Define an empty Reviewer object and their voting options in memory only by declaring the "-NoCommit" switch
            $reviewer = New-SCSMObject -Class $reviewerClass -PropertyHashtable @{"ReviewerID" = "{0}"; "Veto" = $false; "MustVote" = $false} -NoCommit
    
            #Relate the empty Reviewer to the Review Activity in memory by declaring the "-NoCommit" switch
            $RAhasReviewer = New-SCSMRelationshipObject -Source $ra -Relationship $raHasReviewerRelClass -Target $reviewer -NoCommit
    
            #Relate the empty Reviewer object to a User in memory by declaring the "-NoCommit" switch
            $reviewerHasUser = New-SCSMRelationshipObject -Source $reviewer -Relationship $raReviewerIsUserRelClass -Target $UserObj -NoCommit
    
            #With everything defined in memory, finally commit/save all of those items in one go to Service Manager
            $RAhasReviewer.Commit()
            $reviewerHasUser.Commit()
        }
    
      }
    
    }
    
    Add-SCSMReviewer -requestID 'SR123456' -childID 7890 -groupName "SCSM_PeerReviewers"
    

    As you see the function itself is a bit shorter now and I also got rid of the "-computername..." parameter.

    If you use functions or write scripts, I can recommend to place variable assignments on top, be it classes, relationships or whatsoever - Makes it a lot more readable. Seperate classes and relationships too, and it is way more easier to find mistakes in the script ;)

  • Bryan_LalorBryan_Lalor Customer IT Monkey ✭
    Answer ✓

    I'll play with the above and see where that get's me. I was able to get it up and running using the below script that uses the object guid to relate to the objects in SCSM.


    function Add-SCSMReviewer ($ReviewActivityID, $SCSMUser, $Computername)

    {

        #Server Name

        $ComputerName = ""

        $srClass = Get-SCSMClass -name "System.WorkItem.ServiceRequest$" -computername $ComputerName

        $Srequest = Get-SCSMObject -class $srClass -filter "Name -eq SR53413" -computername $ComputerName

        #Finding RA Name

        $containsActivity = Get-scsmrelationshipclass -Name 'System.WorkItemContainsActivity$'

        $rarray = (Get-scsmrelationshipobject -bysource $SRequest -filter "RelationshipId -eq '$($containsActivity.Id)'" | ? {$_.TargetObject.ClassName -eq 'System.WorkItem.Activity.ReviewActivity'}).TargetObject

        $ReviewActivityID = $Rarray.name[0]

        #User Array

        $SCSMPRarray = get-ADGroupMember -Identity "SCSM_PeerReviewers" | Select objectGuid

        $SCSMUser = ($SCSMPRarray).objectGuid.guid


        #Get the classes we need to work with

        $raClass = Get-SCSMClass -name "System.WorkItem.Activity.ReviewActivity$" -ComputerName $Computername

        $reviewerClass = Get-SCSMClass -name "System.Reviewer$" -ComputerName $Computername

        $raHasReviewerRelClass = Get-SCSMRelationshipClass "System.ReviewActivityHasReviewer$" -ComputerName $Computername

        $raReviewerIsUserRelClass = Get-SCSMRelationshipClass "System.ReviewerIsUser$" -ComputerName $Computername

        #Get the Review Activity to update

        $ra = Get-SCSMObject -class $raClass -filter "Name -eq '$ReviewActivityID'" -ComputerName $Computername

        #Loop through the incoming array/list of users

        foreach ($user in $SCSMUser)

        {

            $UserObj = Get-SCSMObject (Get-SCSMCLass -Name System.Domain.User$) | Where-Object{$_.objectGuid -eq $User}

            #Define an empty Reviewer object and their voting options in memory only by declaring the "-NoCommit" switch

            $reviewer = New-SCSMObject -Class $reviewerClass -PropertyHashtable @{"ReviewerID" = "{0}"; "Veto" = $false; "MustVote" = $false} -ComputerName $Computername -NoCommit

            #Relate the empty Reviewer to the Review Activity in memory by declaring the "-NoCommit" switch

            $RAhasReviewer = New-SCSMRelationshipObject -Source $ra -Relationship $raHasReviewerRelClass -Target $reviewer -ComputerName $Computername -NoCommit

            #Relate the empty Reviewer object to a User in memory by declaring the "-NoCommit" switch

            $reviewerHasUser = New-SCSMRelationshipObject -Source $reviewer -Relationship $raReviewerIsUserRelClass -Target $UserObjRel -ComputerName $Computername -NoCommit

            #With everything defined in memory, finally commit/save all of those items in one go to Service Manager

            $RAhasReviewer.Commit()

            $reviewerHasUser.Commit()

        }

    }


    Add-SCSMReviewer -ReviewActivityID $ReviewActivityID -SCSMUser $user


    I was considering making some changes to have the group name be a dynamic pull from the RA as it is already set as a reviewer. I'll make the group not mail-enabled, so Reviewers aren't double notified. And that way I'll only need to maintain one script and it can be used for any future Reviewer groups.


    I did look at using the child ID, but I found it wasn't consistent between RA's. Looking at the same RA for two different SRs showed two different child IDs for the same RA in the template. I'll take a look at that again to see, if I'm missing something in the concept. I appreciate your time @Simon_Zeinhofer .

Answers

  • Patrick_HammerPatrick_Hammer Member IT Monkey ✭
    edited September 2023

    If I understand you, you a searching for somewhing like that:

    $ras = Get-SCSMRelationshipobject -bysource $SRs | ? {$_.targetobject.classname -eq 'ReviewActivity'}
    $ras.targetobject.name
    

    hth

  • Bryan_LalorBryan_Lalor Customer IT Monkey ✭

    I'll give it a shot and get back with you, thank you.

  • Bryan_LalorBryan_Lalor Customer IT Monkey ✭
    edited September 2023
    $Srequest = Get-SCSMObject -class $srClass -filter "Name -eq SR53413" -computername $ComputerName
    $ras = Get-SCSMRelationshipobject -bysource $SRequest | ? {$_.targetobject.classname -eq 'ReviewActivity'}
    
    $ras.targetobject.name
    


    I tried running what you suggested, but it didn't return a value when it's called on the last line.

  • Simon_ZeinhoferSimon_Zeinhofer Customer Advanced IT Monkey ✭✭✭
    edited September 2023 Answer ✓

    @Bryan_Lalor "Get-scsmrelationshipobject" always returns back the relationship object itself. So to target at the source or target you have to write it this way:

    $containsActivity = Get-scsmrelationshipclass -Name 'System.WorkItemContainsActivity$'
    $ra = (Get-scsmrelationshipobject -bysource $SRequest -filter "RelationshipId -eq '$($containsActivity.Id)'" | ? {$_.TargetObject.ClassName -eq 'System.WorkItem.Activity.ReviewActivity'}).TargetObject
    
    


    There are some "problems" with that code though:

    1. If you have more than one RA in that SR template, you would receive an array of RAs, and therefor you would have to iterate through all of them, to find the correct one, else your code to add a reviewer object would fail
    2. A targetobject object is different from receiving the object itself. E.g. if you get the RA directly, you can access all the fields, when using a targetobject, only certain fields, like ID and Name are accessible directly, other values have to be received in a more complicated way. But you can use this targetobject if you want to change a value in it or use it to create a relationship.

    So if you really want to access the Review Activity directly, there is a much better way to do so:

    At first you have to receive the Child ID of the Activity. This ID is unique and can only be used once in all templates. So if your RA has Child ID 25, no other Activity in any template can have that.

    To receive the child ID, you can either

    1. create a dummy SR by that template (after creation skip all activities in it, so nothing can happen, and then save the SR)

    To receive the child ID you can copy the Name of the RA and open it in the SCSM console:


    Inside the activity go to the History tab and collapse the first entry


    There you find the Child Id


    or the much easier way:

    You use this script to receive all Child Ids from a template:

    #$scsm is your SCSM server name
    #$templateName is the name of your SR template
    $scsm = 'SCSM SERVER'
    
    $activityTemplates = [System.Collections.ArrayList]::new()
    $activityTemplates.Add("SRName,ActivityName,ChildID")
    $templateName = "YOUR TEMPLATE NAME"
    
    Function Get-Activities
    {
    
      Param(
         
        $Template,
        $ObjectCollection
    
      )
    
    
      $aHashtable = @{}
      $pCollection = $ObjectCollection.PropertyCollection
      foreach($pr in $pCollection)
      {
    
        $aHashtable.Add($pr.Path,$pr.MixedValue)
    
      }
    
      $aString = $Template.DisplayName + "," + $aHashTable.'$Context/Property[Type=''CustomSystem_WorkItem_Library!System.WorkItem'']/Title$' + "," + $aHashTable.'$Context/Property[Type=''CustomSystem_WorkItem_Activity_Library!System.WorkItem.Activity'']/ChildId$'
      $activityTemplates.Add($aString)
    
    }
    
    Function Get-SAsAndPAs{
    
      Param(
         
        $Template,
        $ObjectCollection
    
      )
    
      $objColl = $ObjectCollection.ObjectCollection
      foreach($obj in $objColl)
      {
    
        if((-not $obj.Path.Contains("TypeConstraint='CustomSystem_WorkItem_Activity_Library!System.WorkItem.Activity.SequentialActivity'")) -and (-not $obj.Path.Contains("TypeConstraint='CustomSystem_WorkItem_Activity_Library!System.WorkItem.Activity.ParallelActivity'")))
        {
    
          Get-Activities -Template $Template -ObjectCollection $obj
    
        }
         
        elseif(($obj.Path.Contains("TypeConstraint='CustomSystem_WorkItem_Activity_Library!System.WorkItem.Activity.SequentialActivity'")) -or ($obj.Path.Contains("TypeConstraint='CustomSystem_WorkItem_Activity_Library!System.WorkItem.Activity.ParallelActivity'")))
        {
    
          Get-SAsAndPAs -Template $Template -ObjectCollection $obj
    
        }
    
      }
    
    
    }
    
    
    
    
    
    
    $temp = Get-scsmobjecttemplate -ComputerName $scsm | ? { $_.DisplayName -like "*$templateName*" }
    
    
      foreach($o in $temp.ObjectCollection)
      {
    
      if((-not $o.Path.Contains("TypeConstraint='CustomSystem_WorkItem_Activity_Library!System.WorkItem.Activity.SequentialActivity'")) -and (-not $o.Path.Contains("TypeConstraint='CustomSystem_WorkItem_Activity_Library!System.WorkItem.Activity.ParallelActivity'")))
      {
         
          Get-Activities -Template $temp -ObjectCollection $o
    
      }
      elseif(($o.Path.Contains("TypeConstraint='CustomSystem_WorkItem_Activity_Library!System.WorkItem.Activity.SequentialActivity'")) -or ($o.Path.Contains("TypeConstraint='CustomSystem_WorkItem_Activity_Library!System.WorkItem.Activity.ParallelActivity'")))
      {
           
           
          Get-SAsAndPAs -Template $temp -ObjectCollection $o
         
      }
         
      }
    

       

       just type in $activityTemplates after it ran through and you will receive a list of all Activities and their Child Ids inside that template.

    If you have the child ID you can use the function "Get-scsmrelatedobject" to receive the RA you need. This function really returns the Activity object and not a targetobject like before:

    $containsActivity = Get-scsmrelationshipclass -Name 'System.WorkItemContainsActivity$'
    $childID = 'YOUR CHILDID'
    $RA = Get-scsmrelatedobject -smobject $SRequest -relationship $containsActivity -Depth Recursive | ? {$_.ChildId -eq $childId}
    
    

    That's it, then you can use the $RA object to add the reviewer object and so on.

    If you have any questions, just ask ;)

  • Adam_DzyackyAdam_Dzyacky Product Owner Contributor Monkey ✭✭✭✭✭
    edited September 2023

    Wow, less than 24 hours and I really feel late to this one 😁

    The original code was looking for an RA Id such as RA1234 via the $ReviewActivityID parameter so the $ra variable could be set. The $ra variable would then represent a single Review Activity. But I think as no doubt others have seen, there are a couple ways you could obtain this variable.

    Still catching up but a lot of good advice above.

  • Bryan_LalorBryan_Lalor Customer IT Monkey ✭
    edited September 2023

    Thanks Simon, I've made some headway using:

    $Srequest = Get-SCSMObject -class $srClass -filter "Name -eq SR53413" -computername $ComputerName
    $containsActivity = Get-scsmrelationshipclass -Name 'System.WorkItemContainsActivity$'
    $ra = (Get-scsmrelationshipobject -bysource $SRequest -filter "RelationshipId -eq '$($containsActivity.Id)'" | ? {$_.TargetObject.ClassName -eq 'System.WorkItem.Activity.ReviewActivity'}).TargetObject
    $Ra.name[0]
    

    I'll need to test it in action to make sure it's pulling the right RA everytime. There are two separate RAs, and i'm not confident the order will show up the same way every time in the array.

    I got it to successfully add reviewers, but it had all of the reviewers as blank values, so It's having issues when I pass it the samaccountname as an array.

    $SCSMPRarray = get-ADGroupMember -Identity "SCSM_PeerReviewers" | Select SamAccountName
    $SCSMUser = ($SCSMPRarray).SamAccountName
    

    I must need to pass those values into something scsm is able to reference correctly. Any insight on that?

  • Bryan_LalorBryan_Lalor Customer IT Monkey ✭

    Here is the red text it is throwing ::

    New-SCSMRelationshipObject : Cannot bind parameter 'Target'. Cannot convert the "Jose.Sankoorikkal" value of type "System.String" to type "Microsoft.EnterpriseManagement.Common.EnterpriseManagementObject".

    At line:30 char:121

    + ... eviewer -Relationship $raReviewerIsUserRelClass -Target $user -Comput ...

    +                               ~~~~~

      + CategoryInfo     : InvalidArgument: (:) [New-SCSMRelationshipObject], ParameterBindingException

      + FullyQualifiedErrorId : CannotConvertArgumentNoMessage,SMLets.NewSCSMRelationshipObject


    You cannot call a method on a null-valued expression.

    At line:33 char:9

    +     $reviewerHasUser.Commit()

    +     ~~~~~~~~~~~~~~~~~~~~~~~~~

      + CategoryInfo     : InvalidOperation: (:) [], RuntimeException

      + FullyQualifiedErrorId : InvokeMethodOnNull


    Here is the full script thus far::

    function Add-SCSMReviewer ($ReviewActivityID, $SCSMUser, $Computername)

    {

        #Server Name

        $ComputerName = "SM-0001"

        $srClass = Get-SCSMClass -name "System.WorkItem.ServiceRequest$" -computername $ComputerName

        $Srequest = Get-SCSMObject -class $srClass -filter "Name -eq SR53413" -computername $ComputerName

        #Finding RA Name

        $containsActivity = Get-scsmrelationshipclass -Name 'System.WorkItemContainsActivity$'

        $rarray = (Get-scsmrelationshipobject -bysource $SRequest -filter "RelationshipId -eq '$($containsActivity.Id)'" | ? {$_.TargetObject.ClassName -eq 'System.WorkItem.Activity.ReviewActivity'}).TargetObject

        $ReviewActivityID = $Rarray.name[0]

        #User Array

        $SCSMPRarray = get-ADGroupMember -Identity "SCSM_PeerReviewers" | Select SamAccountName

        $SCSMUser = ($SCSMPRarray).SamAccountName


        #Get the classes we need to work with

        $raClass = Get-SCSMClass -name "System.WorkItem.Activity.ReviewActivity$" -ComputerName $Computername

        $reviewerClass = Get-SCSMClass -name "System.Reviewer$" -ComputerName $Computername

        $raHasReviewerRelClass = Get-SCSMRelationshipClass "System.ReviewActivityHasReviewer$" -ComputerName $Computername

        $raReviewerIsUserRelClass = Get-SCSMRelationshipClass "System.ReviewerIsUser$" -ComputerName $Computername

        #Get the Review Activity to update

        $ra = Get-SCSMObject -class $raClass -filter "Name -eq '$ReviewActivityID'" -ComputerName $Computername

        #Loop through the incoming array/list of users

        foreach ($user in $SCSMUser)

        {

            #Define an empty Reviewer object and their voting options in memory only by declaring the "-NoCommit" switch

            $reviewer = New-SCSMObject -Class $reviewerClass -PropertyHashtable @{"ReviewerID" = "{0}"; "Veto" = $false; "MustVote" = $false} -ComputerName $Computername -NoCommit

            #Relate the empty Reviewer to the Review Activity in memory by declaring the "-NoCommit" switch

            $RAhasReviewer = New-SCSMRelationshipObject -Source $ra -Relationship $raHasReviewerRelClass -Target $reviewer -ComputerName $Computername -NoCommit

            #Relate the empty Reviewer object to a User in memory by declaring the "-NoCommit" switch

            $reviewerHasUser = New-SCSMRelationshipObject -Source $reviewer -Relationship $raReviewerIsUserRelClass -Target $user -ComputerName $Computername -NoCommit

            #With everything defined in memory, finally commit/save all of those items in one go to Service Manager

            $RAhasReviewer.Commit()

            $reviewerHasUser.Commit()

        }

    }

    $userClass = Get-SCSMClass -name "System.Domain.User$"

    Add-SCSMReviewer -ReviewActivityID $ReviewActivityID -SCSMUser $user

  • Bryan_LalorBryan_Lalor Customer IT Monkey ✭

    The below worked most of the way. It added all but 3 users who it left with blank values. They all have displaynames in AD. I'm trying to instead key off of objectGuids instead of displaynames, but I haven't had any luck so far with getting it to take a ObjectGuid for the $UserObj Variable

    function Add-SCSMReviewer ($ReviewActivityID, $SCSMUser, $Computername)

    {

      #Server Name

      $ComputerName = "SM-0005"

      $srClass = Get-SCSMClass -name "System.WorkItem.ServiceRequest$" -computername $ComputerName

      $Srequest = Get-SCSMObject -class $srClass -filter "Name -eq SR53413" -computername $ComputerName

      #Finding RA Name

      $containsActivity = Get-scsmrelationshipclass -Name 'System.WorkItemContainsActivity$'

      $rarray = (Get-scsmrelationshipobject -bysource $SRequest -filter "RelationshipId -eq '$($containsActivity.Id)'" | ? {$_.TargetObject.ClassName -eq 'System.WorkItem.Activity.ReviewActivity'}).TargetObject

      $ReviewActivityID = $Rarray.name[0]

      #User Array

      $SCSMPRarray = get-ADGroupMember -Identity "SCSM_PeerReviewers" | Select Name

      $SCSMUser = ($SCSMPRarray).Name

      # $SCSMUser = $SCSMUser | ForEach-Object {"[email protected]"}


      #Get the classes we need to work with

      $raClass = Get-SCSMClass -name "System.WorkItem.Activity.ReviewActivity$" -ComputerName $Computername

      $reviewerClass = Get-SCSMClass -name "System.Reviewer$" -ComputerName $Computername

      $raHasReviewerRelClass = Get-SCSMRelationshipClass "System.ReviewActivityHasReviewer$" -ComputerName $Computername

      $raReviewerIsUserRelClass = Get-SCSMRelationshipClass "System.ReviewerIsUser$" -ComputerName $Computername

      #Get the Review Activity to update

      $ra = Get-SCSMObject -class $raClass -filter "Name -eq '$ReviewActivityID'" -ComputerName $Computername

      #Loop through the incoming array/list of users

      foreach ($user in $SCSMUser)

      {

        $UserOBj = Get-SCSMObject (Get-SCSMCLass -Name System.Domain.User$) -Filter "Displayname -eq $User"

        #Define an empty Reviewer object and their voting options in memory only by declaring the "-NoCommit" switch

        $reviewer = New-SCSMObject -Class $reviewerClass -PropertyHashtable @{"ReviewerID" = "{0}"; "Veto" = $false; "MustVote" = $false} -ComputerName $Computername -NoCommit

        #Relate the empty Reviewer to the Review Activity in memory by declaring the "-NoCommit" switch

        $RAhasReviewer = New-SCSMRelationshipObject -Source $ra -Relationship $raHasReviewerRelClass -Target $reviewer -ComputerName $Computername -NoCommit

        #Relate the empty Reviewer object to a User in memory by declaring the "-NoCommit" switch

        $reviewerHasUser = New-SCSMRelationshipObject -Source $reviewer -Relationship $raReviewerIsUserRelClass -Target $UserObj -ComputerName $Computername -NoCommit

        #With everything defined in memory, finally commit/save all of those items in one go to Service Manager

        $RAhasReviewer.Commit()

        $reviewerHasUser.Commit()

      }

    }


    Add-SCSMReviewer -ReviewActivityID $ReviewActivityID -SCSMUser $user 

  • Bryan_LalorBryan_Lalor Customer IT Monkey ✭

    Found for sure that using display name is the problem, It's drawing two users for one of the display names, the other two users use nick names, and that is why they aren't showing up.

  • Patrick_HammerPatrick_Hammer Member IT Monkey ✭

    So this is the solution or are there questions left?

  • Simon_ZeinhoferSimon_Zeinhofer Customer Advanced IT Monkey ✭✭✭
    edited September 2023 Answer ✓

    @Bryan_Lalor

    For the RA part, normally if you receive more than one, the order should always be the same. But again, I would always prefer using the Child ID for that - We use the Child ID for adding reviewers dynamically, in Subscriptions and so on, and for us it works nearly 100 % of the time.

    As far as the User objects go: You could use the username (Samaccountname in AD) as domain/username is the primary key in the SCSM database, so it can only exist once - So you should receive exactly the user you need, even if there is one or more users with the same displayname.

    So if you say

    $SCSMPRarray = get-ADGroupMember -Identity "SCSM_PeerReviewers"
    $userClass = Get-scsmclass -name 'Microsoft.AD.User$'
    $scsmUser = @() 
    foreach($a in $SCSMPRarray)
    {
        $scsmUser += (Get-scsmobject -class $userClass -filter "Username -eq '$($a.Samaccountname)'")
    }
    

    you first receive all AD users, then create an empty array and then, with an iteration, you fill the array with the corresponding SCSM User. Now in your Add-SCSMReviewer function you can directly iterate through the array with the userobjects in it and add them to the reviewer objects - Or, to limit the number of iterations, you could rewrite the function. I did a little change and the function looks like this now (I hope @Adam_Dzyacky is not mad at me for doing so 😉):

    <#parameter $requestID is the ID SR* of your service request
    parameter $childID is the child ID of the RA you need
    parameter $groupname is the samaccountname of your AD group. So if you need another group,
    you only need to change the name in the function call
    #>
     
    function Add-SCSMReviewer ($requestID,$childID, $groupName)
    
    {
    
      #Server Name
      $SMDefaultComputer = "SCSMSERVER"
    
      $userClass = Get-scsmclass -name 'Microsoft.AD.User$'
      $srClass = Get-SCSMClass -name "System.WorkItem.ServiceRequest$"
      $reviewerClass = Get-SCSMClass -name "System.Reviewer$"
    
      $containsActivity = Get-scsmrelationshipclass -Name 'System.WorkItemContainsActivity$'
      $raHasReviewerRelClass = Get-SCSMRelationshipClass "System.ReviewActivityHasReviewer$"
      $raReviewerIsUserRelClass = Get-SCSMRelationshipClass "System.ReviewerIsUser$"
     
      $Srequest = Get-SCSMObject -class $srClass -filter "Name -eq '$($requestID)'"
      $ReviewActivity = Get-scsmrelatedobject -smobject $SRequest -relationship $containsActivity -Depth Recursive | ? {$_.ChildId -eq $childId}
      $SCSMPRarray = get-ADGroupMember -Identity $groupName
    
      foreach ($user in $SCSMPRarray)
    
      {
    
        $UserOBj = Get-SCSMObject -Class $userClass -filter "Username -eq '$($user.Samaccountname)'"
        
        if($UserOBj)
        {
    
            #Define an empty Reviewer object and their voting options in memory only by declaring the "-NoCommit" switch
            $reviewer = New-SCSMObject -Class $reviewerClass -PropertyHashtable @{"ReviewerID" = "{0}"; "Veto" = $false; "MustVote" = $false} -NoCommit
    
            #Relate the empty Reviewer to the Review Activity in memory by declaring the "-NoCommit" switch
            $RAhasReviewer = New-SCSMRelationshipObject -Source $ra -Relationship $raHasReviewerRelClass -Target $reviewer -NoCommit
    
            #Relate the empty Reviewer object to a User in memory by declaring the "-NoCommit" switch
            $reviewerHasUser = New-SCSMRelationshipObject -Source $reviewer -Relationship $raReviewerIsUserRelClass -Target $UserObj -NoCommit
    
            #With everything defined in memory, finally commit/save all of those items in one go to Service Manager
            $RAhasReviewer.Commit()
            $reviewerHasUser.Commit()
        }
    
      }
    
    }
    
    Add-SCSMReviewer -requestID 'SR123456' -childID 7890 -groupName "SCSM_PeerReviewers"
    

    As you see the function itself is a bit shorter now and I also got rid of the "-computername..." parameter.

    If you use functions or write scripts, I can recommend to place variable assignments on top, be it classes, relationships or whatsoever - Makes it a lot more readable. Seperate classes and relationships too, and it is way more easier to find mistakes in the script ;)

  • Bryan_LalorBryan_Lalor Customer IT Monkey ✭
    Answer ✓

    I'll play with the above and see where that get's me. I was able to get it up and running using the below script that uses the object guid to relate to the objects in SCSM.


    function Add-SCSMReviewer ($ReviewActivityID, $SCSMUser, $Computername)

    {

        #Server Name

        $ComputerName = ""

        $srClass = Get-SCSMClass -name "System.WorkItem.ServiceRequest$" -computername $ComputerName

        $Srequest = Get-SCSMObject -class $srClass -filter "Name -eq SR53413" -computername $ComputerName

        #Finding RA Name

        $containsActivity = Get-scsmrelationshipclass -Name 'System.WorkItemContainsActivity$'

        $rarray = (Get-scsmrelationshipobject -bysource $SRequest -filter "RelationshipId -eq '$($containsActivity.Id)'" | ? {$_.TargetObject.ClassName -eq 'System.WorkItem.Activity.ReviewActivity'}).TargetObject

        $ReviewActivityID = $Rarray.name[0]

        #User Array

        $SCSMPRarray = get-ADGroupMember -Identity "SCSM_PeerReviewers" | Select objectGuid

        $SCSMUser = ($SCSMPRarray).objectGuid.guid


        #Get the classes we need to work with

        $raClass = Get-SCSMClass -name "System.WorkItem.Activity.ReviewActivity$" -ComputerName $Computername

        $reviewerClass = Get-SCSMClass -name "System.Reviewer$" -ComputerName $Computername

        $raHasReviewerRelClass = Get-SCSMRelationshipClass "System.ReviewActivityHasReviewer$" -ComputerName $Computername

        $raReviewerIsUserRelClass = Get-SCSMRelationshipClass "System.ReviewerIsUser$" -ComputerName $Computername

        #Get the Review Activity to update

        $ra = Get-SCSMObject -class $raClass -filter "Name -eq '$ReviewActivityID'" -ComputerName $Computername

        #Loop through the incoming array/list of users

        foreach ($user in $SCSMUser)

        {

            $UserObj = Get-SCSMObject (Get-SCSMCLass -Name System.Domain.User$) | Where-Object{$_.objectGuid -eq $User}

            #Define an empty Reviewer object and their voting options in memory only by declaring the "-NoCommit" switch

            $reviewer = New-SCSMObject -Class $reviewerClass -PropertyHashtable @{"ReviewerID" = "{0}"; "Veto" = $false; "MustVote" = $false} -ComputerName $Computername -NoCommit

            #Relate the empty Reviewer to the Review Activity in memory by declaring the "-NoCommit" switch

            $RAhasReviewer = New-SCSMRelationshipObject -Source $ra -Relationship $raHasReviewerRelClass -Target $reviewer -ComputerName $Computername -NoCommit

            #Relate the empty Reviewer object to a User in memory by declaring the "-NoCommit" switch

            $reviewerHasUser = New-SCSMRelationshipObject -Source $reviewer -Relationship $raReviewerIsUserRelClass -Target $UserObjRel -ComputerName $Computername -NoCommit

            #With everything defined in memory, finally commit/save all of those items in one go to Service Manager

            $RAhasReviewer.Commit()

            $reviewerHasUser.Commit()

        }

    }


    Add-SCSMReviewer -ReviewActivityID $ReviewActivityID -SCSMUser $user


    I was considering making some changes to have the group name be a dynamic pull from the RA as it is already set as a reviewer. I'll make the group not mail-enabled, so Reviewers aren't double notified. And that way I'll only need to maintain one script and it can be used for any future Reviewer groups.


    I did look at using the child ID, but I found it wasn't consistent between RA's. Looking at the same RA for two different SRs showed two different child IDs for the same RA in the template. I'll take a look at that again to see, if I'm missing something in the concept. I appreciate your time @Simon_Zeinhofer .

  • Simon_ZeinhoferSimon_Zeinhofer Customer Advanced IT Monkey ✭✭✭

    As I mentioned the Child ID may only exist once in your environment ;-) So even if you add the exact same RA to two different templates, the child ID will differ.

    But if you have e.g. 3 different SR templates, which should use the same RA and you know all 3 child IDs, you could use the following

    $ReviewActivity = Get-scsmrelatedobject -smobject $SRequest -relationship $containsActivity -Depth Recursive | ? {$_.ChildId -in 'ChildID1','ChildID2','ChildID3'}
    

    You can then delete the child ID parameter from the function call, as it is not needed anymore. As the Child ID only exists once in your SR, it will return the correct RA for that SR template.

    Glad I could help :)

Sign In or Register to comment.