New: Set-PrimaryOwner.ps1

Mar 4, 2011 at 12:29 AM

Building on your Set-IncidentUser script, here's a Set-PrimaryOwner.ps1 script:

#requires -version 2.0
[CmdletBinding(SupportsShouldProcess=$true)]
param (
    [Parameter()][string]$PrimaryOwner,
    [Parameter(ValueFromPipeline=$true)]$TheIncident
)
BEGIN
{
    $NS = "Microsoft.EnterpriseManagement"
    $EMGType = "${NS}.EnterpriseManagementGroup"
    $EMG = new-object ${EMGType} localhost
 
    $PRIMARYOWNERRELATIONSHIP = $EMG.EntityTypes.GetRelationshipClasses()|?{$_.name -eq "System.WorkItem.IncidentPrimaryOwner"}
    $DEFAULT = ("${NS}.Common.ObjectQueryOptions" -as "type")::Default
    $EMOT    = "${NS}.Common.EnterpriseManagementObject" -as "type"
    $EMOP = "EnterpriseManagementObjectProjection"
    $IPT = "System.WorkItem.Incident.ProjectionType"
    $CEMO = "${NS}.Common.CreatableEnterpriseManagementObject"
    $INCIDENTC = $EMG.EntityTypes.GetClasses() |?{$_.name -eq "System.WorkItem.Incident" }
    $COMMENTC = $EMG.EntityTypes.GetClasses()|?{$_.name -eq "System.WorkItem.TroubleTicket.AnalystCommentLog"}
    $USERC    = $EMG.EntityTypes.GetClasses()|?{$_.name -eq "System.User"}
    $PROJECTION = $EMG.EntityTypes.GetTypeProjections()| ?{$_.name -eq $IPT}
    $MPCLASSTYPE = "${NS}.Configuration.ManagementPackClass"
    $SYSTEMMP = $EMG.ManagementPacks.GetManagementPacks()|?{$_.name -eq "System.Library"}

    function Get-User
    {
 
        param ( 
            [parameter(Mandatory=$true,Position=0)][string]$displayname
            )
            $criteriaString = '
     <Criteria xmlns="http://Microsoft.EnterpriseManagement.Core.Criteria/">
      <Reference Id="System.Library" Version="{0}" PublicKeyToken="{1}" Alias="System" />
      <Expression>
        <SimpleExpression>
          <ValueExpressionLeft><Property>$Target/Property[Type=''System!System.Domain.User'']/DisplayName$</Property></ValueExpressionLeft>
          <Operator>Equal</Operator>
          <ValueExpressionRight><Value>{2}</Value></ValueExpressionRight>
        </SimpleExpression>
      </Expression>
    </Criteria>
    ' -f $SYSTEMMP.Version, $SYSTEMMP.KeyToken, $displayname
        $userclass = get-scsmclass System.Domain.User
        $CriteriaType = "Microsoft.EnterpriseManagement.Common.EnterpriseManagementObjectCriteria"
        $global:criteria = new-object $CriteriaType $criteriastring,$userclass,$EMG
        $r = Get-SCSMObject -criteria $criteria 
        $r

    }
}
PROCESS
{
        $incident = $TheIncident.__base


        if ( $PrimaryOwner )
        {
            $User = Get-User $PrimaryOwner
            $Incident.Add(($User -as $EMOT) ,$PRIMARYOWNERRELATIONSHIP.Target)
        }

        if ( $PSCmdlet.ShouldProcess($TheIncident.DisplayName))
        {
            $Incident.Commit()
        }

}

Example:

# Change all incidents with an empty Primary Owner so that the Primary Owner becomes equal to the user who created the incident.

get-scsmincident|where{$_.relatestoincident -eq $null}|foreach{set-primaryowner -theincident $_ -primaryowner $_.createdworkitem.displayname}}

Developer
Mar 4, 2011 at 5:59 AM

nice one!

Coordinator
Mar 4, 2011 at 2:02 PM

Nice work. There are some optimizations you could make to the script though. For example, there is a lot of where-object (?) filtering going on in this script which means you are retrieving a lot of data from the SDK and then filteing through it on the client side. You could reduce the amount of data traveling over the wire and make the SQL Server do more of the work insteaad of the client looping through things.

$PRIMARYOWNERRELATIONSHIP = $EMG.EntityTypes.GetRelationshipClasses()|?{$_.name -eq "System.WorkItem.IncidentPrimaryOwner"}
should be

$PRIMARYOWNERRELATIONSHIP = $EMG.EntityTypes.GetRelationshipClass([guid]'')

You can do the same for getting classes, type projections, and management packs.

To determine the GUID I just look it up in the database using queries like these:

select * from RelationshipType where RelationshipTypeName = 'System.WorkItem.IncidentPrimaryOwner'

select * from ManagedType where TypeName = 'System.User'

select * from TypeProjection where TypeProjectionName = 'System.WorkItem.Incident.ProjectionType'

select * from ManagementPack where MPName = 'System.Library'

 

You may also want to consider changing the paramter from being display name of the user to domain\username or -domain and -username.  The key properties for the user class are domain + username so if you use those in your criteria you will always be assured of getting back only a single user.

 

 

Here is an updated script:

 

[CmdletBinding(SupportsShouldProcess=$true)]
param (
    [Parameter()][string]$PrimaryOwner,
    [Parameter(ValueFromPipeline=$true)]$TheIncident
)
BEGIN
{
    [reflection.assembly]::LoadWithPartialName("Microsoft.EnterpriseManagement.Core")
    $NS = "Microsoft.EnterpriseManagement"
    $EMGType = "${NS}.EnterpriseManagementGroup"
    $EMG = new-object ${EMGType} localhost
 
    $PRIMARYOWNERRELATIONSHIP = $EMG.EntityTypes.GetRelationshipClass([guid]'42179172-3D24-CFC8-3944-B0A18F550214')
    $DEFAULT = ("${NS}.Common.ObjectQueryOptions" -as "type")::Default
    $EMOT    = "${NS}.Common.EnterpriseManagementObject" -as "type"
    $EMOP = "EnterpriseManagementObjectProjection"
    $CEMO = "${NS}.Common.CreatableEnterpriseManagementObject"
    $INCIDENTC = $EMG.EntityTypes.GetClass([guid]'A604B942-4C7B-2FB2-28DC-61DC6F465C68') #System.WorkItem.Incident
    $COMMENTC = $EMG.EntityTypes.GetClass([guid]'F14B70F4-878C-C0E1-B5C1-06CA22D05D40') #System.WorkItem.TroubleTicket.AnalystCommentLog
    $USERC    = $EMG.EntityTypes.GetClass([guid]'943D298F-D79A-7A29-A335-8833E582D252') #System.User
    $PROJECTION = $EMG.EntityTypes.GetTypeProjection([guid]'285CB0A2-F276-BCCB-563E-BB721DF7CDEC') #System.WorkItem.Incident.ProjectionType
    $MPCLASSTYPE = "${NS}.Configuration.ManagementPackClass"
    $SYSTEMMP = $EMG.ManagementPacks.GetManagementPack([guid]'01C8B236-3BCE-9DBA-6F1C-C119BCDC2972') #System.Library

    function Get-User
    {
 
        param ( 
            [parameter(Mandatory=$true,Position=0)][string]$displayname
            )
            $criteriaString = '
     <Criteria xmlns="http://Microsoft.EnterpriseManagement.Core.Criteria/">
      <Reference Id="System.Library" Version="{0}" PublicKeyToken="{1}" Alias="System" />
      <Expression>
        <SimpleExpression>
          <ValueExpressionLeft><Property>$Target/Property[Type=''System!System.Domain.User'']/DisplayName$</Property></ValueExpressionLeft>
          <Operator>Equal</Operator>
          <ValueExpressionRight><Value>{2}</Value></ValueExpressionRight>
        </SimpleExpression>
      </Expression>
    </Criteria>
    ' -f $SYSTEMMP.Version, $SYSTEMMP.KeyToken, $displayname
        $userclass = get-scsmclass System.Domain.User
        $CriteriaType = "Microsoft.EnterpriseManagement.Common.EnterpriseManagementObjectCriteria"
        $global:criteria = new-object $CriteriaType $criteriastring,$userclass,$EMG
        $r = Get-SCSMObject -criteria $criteria 
        $r

    }
}
PROCESS
{
        $incident = $TheIncident.__base


        if ( $PrimaryOwner )
        {
            $User = Get-User $PrimaryOwner
            $Incident.Add(($User -as $EMOT) ,$PRIMARYOWNERRELATIONSHIP.Target)
        }

        if ( $PSCmdlet.ShouldProcess($TheIncident.DisplayName))
        {
            $Incident.Commit()
        }

}

 

Apr 3, 2011 at 12:33 AM

I tried updating the criteria to include both username and domain, but I receive an XML parse error:

New-Object : Exception calling ".ctor" with "3" argument(s): "The criteria could not be parsed. See inner exception for
 details."
At C:\Windows\system32\WindowsPowerShell\v1.0\Modules\smlets\Scripts\Set-IncidentUser2.ps1:59 char:31
+         $criteria = new-object <<<<  $CriteriaType $criteriastring,$userclass,$EMG
    + CategoryInfo          : InvalidOperation: (:) [New-Object], MethodInvocationException
    + FullyQualifiedErrorId : ConstructorInvokedThrowException,Microsoft.PowerShell.Commands.NewObjectCommand

Here is the code fragment I modified to test against a single domain:

          $criteriaString = '
     <Criteria xmlns="http://Microsoft.EnterpriseManagement.Core.Criteria/">
      <Reference Id="System.Library" Version="{0}" PublicKeyToken="{1}" Alias="System" />
      <Expression>
 <AND>
        <Expression>
        <SimpleExpression>
          <ValueExpressionLeft><Property>$Target/Property[Type=''System!System.Domain.User'']/DisplayName$</Property></ValueExpressionLeft>
          <Operator>Equal</Operator>
          <ValueExpressionRight><Value>{2}</Value></ValueExpressionRight>
        </SimpleExpression>
       </Expression>   
       <Expression>
        <SimpleExpression>
          <ValueExpressionLeft><Property>$Target/Property[Type=''System!System.Domain.User'']/DisplayName$</Property></ValueExpressionLeft>
          <Operator>Equal</Operator>
          <ValueExpressionRight><Value>"MYDOMAIN"</Value></ValueExpressionRight>
        </SimpleExpression>
       </Expression> 
      </AND>
     </Expression> 
    </Criteria>
    ' -f $SYSTEMMP.Version, $SYSTEMMP.KeyToken, $displayname
        $userclass = get-scsmclass System.Domain.User
        $CriteriaType = "Microsoft.EnterpriseManagement.Common.EnterpriseManagementObjectCriteria"
        $global:criteria = new-object $CriteriaType $criteriastring,$userclass,$EMG
        $r = Get-SCSMObject -criteria $criteria
        $r

Developer
Apr 4, 2011 at 10:35 PM

in this criteria, you actually aren't looking for the domain property (the second Expression is hunting for the same property value as the first one), you should probably try:
<ValueExpressionLeft><Property>$Target/Property[Type=''System!System.Domain.User'']/DisplayName$</Property></ValueExpressionLeft>

should be something like this:
<ValueExpressionLeft><Property>$Target/Property[Type=''System!System.Domain.User'']/Domain$</Property></ValueExpressionLeft>

Also, the criteria XML is case sensitive, so <AND> won't work, it must by <And>. The following should work

$criteriaString = '
     <Criteria xmlns="http://Microsoft.EnterpriseManagement.Core.Criteria/">
      <Reference Id="System.Library" Version="{0}" PublicKeyToken="{1}" Alias="System" />
      <Expression>
 <And>
        <Expression>
        <SimpleExpression>
          <ValueExpressionLeft><Property>$Target/Property[Type=''System!System.Domain.User'']/DisplayName$</Property></ValueExpressionLeft>
          <Operator>Equal</Operator>
          <ValueExpressionRight><Value>{2}</Value></ValueExpressionRight>
        </SimpleExpression>
       </Expression>    
       <Expression>
        <SimpleExpression>
          <ValueExpressionLeft><Property>$Target/Property[Type=''System!System.Domain.User'']/Domain$</Property></ValueExpressionLeft>
          <Operator>Equal</Operator>
          <ValueExpressionRight><Value>MYDOMAIN</Value></ValueExpressionRight>
        </SimpleExpression>
       </Expression>  
      </And>
     </Expression>  
    </Criteria>
' -f $SYSTEMMP.Version, $SYSTEMMP.KeyToken, $displayname
$userclass = get-scsmclass System.Domain.User
$CriteriaType = "Microsoft.EnterpriseManagement.Common.EnterpriseManagementObjectCriteria"
$criteria = new-object $CriteriaType $criteriastring,$userclass,$EMG
$r = Get-SCSMObject -criteria $criteria 
Jul 28, 2011 at 8:59 PM

the script is working great, but i am not able to set this in a custom workflow.

i tried to build one with the authoring console, i triggered it when a new incident is created. but i can't bind the incident properties to the script parameters.

any ideas?

 

Developer
Aug 1, 2011 at 9:41 PM

There are a number of issues that this could be, I'm not sure whether the authoring console knows how to deal with script parameters. Do you know what you're getting in the workflow? It's likely that it is not the objects, but rather guids which represent the object. This means that your script has to retrieve the object and then the script should be able to help. I believe that there are examples floating around for powershell scripts in workflows, that would be the place to start. You may not be able to use the authoring console, so you may have to sling the XML code directly.

Aug 1, 2011 at 9:44 PM

How about making the adsi query work from the sql server management console.

My guess is once the adsi query works in console on usbedprdsql01 it will also work in the workflow.

From: jtruher [email removed]
Sent: Monday, August 01, 2011 4:41 PM
To: Skrede, David
Subject: Re: New: Set-PrimaryOwner.ps1 [smlets:248391]

From: jtruher

There are a number of issues that this could be, I'm not sure whether the authoring console knows how to deal with script parameters. Do you know what you're getting in the workflow? It's likely that it is not the objects, but rather guids which represent the object. This means that your script has to retrieve the object and then the script should be able to help. I believe that there are examples floating around for powershell scripts in workflows, that would be the place to start. You may not be able to use the authoring console, so you may have to sling the XML code directly.

Developer
Aug 1, 2011 at 10:00 PM

ADSI - there's no ADSI going on here, it's all Service Manager.

Aug 1, 2011 at 10:55 PM

Someone setup the ISReporting job step 1 to run as dbo. And someone changed the password on the ADSI connection so it was wrong (which made the open queries fail).

Openqueries to ADSI linked server are working and the ISReporting job now running fine. You can close the ticket.

From: jtruher [email removed]
Sent: Monday, August 01, 2011 5:01 PM
To: Skrede, David
Subject: Re: New: Set-PrimaryOwner.ps1 [smlets:248391]

From: jtruher

ADSI - there's no ADSI going on here, it's all Service Manager.

Oct 1, 2011 at 10:23 PM

Finally got it working.

I made some changes to the code to run when triggered by a new incident created workflow.

param ([string]$IncidentID)
   
    [reflection.assembly]::LoadWithPartialName("Microsoft.EnterpriseManagement.Core")
    $NS = "Microsoft.EnterpriseManagement"
    $EMGType = "${NS}.EnterpriseManagementGroup"
    $EMG = new-object ${EMGType} localhost
 
    $PRIMARYOWNERRELATIONSHIP = $EMG.EntityTypes.GetRelationshipClass([guid]'42179172-3D24-CFC8-3944-B0A18F550214')
    $DEFAULT = ("${NS}.Common.ObjectQueryOptions" -as "type")::Default
    $EMOT    = "${NS}.Common.EnterpriseManagementObject" -as "type"
    $EMOP = "EnterpriseManagementObjectProjection"
    $CEMO = "${NS}.Common.CreatableEnterpriseManagementObject"
    $INCIDENTC = $EMG.EntityTypes.GetClass([guid]'A604B942-4C7B-2FB2-28DC-61DC6F465C68') #System.WorkItem.Incident
    $COMMENTC = $EMG.EntityTypes.GetClass([guid]'F14B70F4-878C-C0E1-B5C1-06CA22D05D40') #System.WorkItem.TroubleTicket.AnalystCommentLog
    $USERC    = $EMG.EntityTypes.GetClass([guid]'943D298F-D79A-7A29-A335-8833E582D252') #System.User
    $PROJECTION = $EMG.EntityTypes.GetTypeProjection([guid]'285CB0A2-F276-BCCB-563E-BB721DF7CDEC') #System.WorkItem.Incident.ProjectionType
    $MPCLASSTYPE = "${NS}.Configuration.ManagementPackClass"
    $SYSTEMMP = $EMG.ManagementPacks.GetManagementPack([guid]'01C8B236-3BCE-9DBA-6F1C-C119BCDC2972') #System.Library
  
 function Get-User
    {
        param ([string]$displayname)
 
            $criteriaString = '
     <Criteria xmlns="http://Microsoft.EnterpriseManagement.Core.Criteria/">
      <Reference Id="System.Library" Version="{0}" PublicKeyToken="{1}" Alias="System" />
      <Expression>
        <SimpleExpression>
          <ValueExpressionLeft><Property>$Context/Property[Type="System!System.Domain.User"]/DisplayName$</Property></ValueExpressionLeft>
          <Operator>Equal</Operator>
          <ValueExpressionRight><Value>{2}</Value></ValueExpressionRight>
        </SimpleExpression>
      </Expression>
    </Criteria>
    ' -f $SYSTEMMP.Version, $SYSTEMMP.KeyToken, $displayname
        $userclass = get-scsmclass System.Domain.User
        $CriteriaType = "Microsoft.EnterpriseManagement.Common.EnterpriseManagementObjectCriteria"
        $global:criteria = new-object $CriteriaType $criteriastring,$userclass,$EMG
        $r = Get-SCSMObject -criteria $criteria
        $r

    }

 

 import-module smlets
 
        $TheIncident = GET-scsmincident -id $incidentid
 $incident=$TheIncident.__base
 
        $User = Get-User $theincident.createdworkitem.DisplayName
       
 $Incident.Add(($User -as $EMOT) ,$PRIMARYOWNERRELATIONSHIP.Target)
        $Incident.Commit()

 remove-module -name SMLets –force

 

and then create a new WF that sends the incident ID to the script on each new incident.

Thanks for your help, hope it's usefull.