Archive for February 2010

Getting Service Manager Incidents with PowerShell   3 comments

So far in these blogs, I’ve not addressed one of the main focuses of Service Manager which is how to manage incidents. In Service Manager, incidents are more than simple instances, they’re what’s called a “projection”. Jakub has some great postings on projections (here and here), so I’m not going to go into detail about what a projection is, but rather how it affects PowerShell. The object model of the projection makes it a little tricky to work with from PowerShell, but can be done.

Here’s what the incident view looks like in the console.

That’s my target – create a script that has output similar to the console.

The script is constructed into two sections:

  • The BEGIN section makes sure that the assemblies we need are loaded and creates functions that the script will use.
    • The code in lines 20 to 50 make sure Microsoft.EnterpriseManagement.Core.dll is loaded and that we have a successful connection to the Data Access Service.
    • The Get-SMIncident function does the actual work of retrieving the incident from the Data Access Service. It’s a little tricky because the Service Manager 2010 SDK uses generics, so we need a bit of reflection to invoke the method. This function also creates the criteria that we use to retrieve the incident. This way we filter on the server side rather than the client side which will be much faster if we have any reasonable number of incidents.
    • The Get-AdaptedEMO function converts the EnterpriseManagementObject to something more idiomatic for PowerShell. I’ve mentioned this before, but since the actual interesting information is actually in the Values property of the EnterpriseManagementObject, this function creates a PSCustom object to which we add NoteProperties. This will help us later when we start formatting.
  • The END section does the work of retrieving the incident and adapting it from how it is retrieved from the Data Access Service to something more useful.
    • This is done by adapting the main object of the projection (the object property on the projection) and the EnterpriseManagementObjects that represent the various relationships which is in line 166 of the script. It takes advantage of the fact that a projection object has an enumerator. Line 166 retrieves the keys which are then used in the foreach loop in 167-174 to retrieve each related object of the projection. That object is then adapted for PowerShell use and added as a property to the PSCustom object which represents the incident.
    • Lines 176-188 promote some of the properties of the main object (the incident’s Object property) as well as some of the related objects properties so we can create the formatting we want more easily.
    • Finally, the adapted incident is output (line 190)
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
#requires -version 2.0
# Get-Incident
# Retrieve Service Manager Incidnents
# The IncidentString may need to include a trailing ‘%’
# examples:
# Get-Incident IR17%
# Retrieves incidents that have a displayname which starts with ‘IR17’
[CmdletBinding(SupportsShouldProcess=$true)]
param ( 
    [Parameter(Position=0)][string]$IncidentString = "%",
    [Parameter()][String]$ComputerName = "localhost",
    [Parameter()]$Credential
    )

# Set up the enviroment
# this creates the functions that we need to get the incident
# and sets up the types and methods we need to work
BEGIN
{
    # Save the NameSpace to save some room
    $NS = "Microsoft.EnterpriseManagement"
    $EMGType = "${NS}.EnterpriseManagementGroup"
    # if we don’t have our EnterpriseManagementObject available, we need to
    # load the assembly
    if ( ! ("${NS}.Common.EnterpriseManagementObject" -as "type"))
    {
        [reflection.assembly]::LoadWithPartialName("${NS}.Core")|out-null
    }

    # Create the connection to the ManagementGroup and use a
    # credential if one was offered.
    if ( $Credential )
    {
        $SETType = "${NS}.EnterpriseManagementConnectionSettings"
        $Settings = new-object $SETType $ComputerName
        $Settings.UserName = $Credential.GetNetWorkCredential().UserName
        $Settings.Domain   = $Credential.GetNetWorkCredential().Domain
        $Settings.Password = $Credential.Password
        $EMG = new-object $EMGType $Settings
    }
    else
    {
        $EMG = new-object $EMGType $ComputerName
    }
    # Be sure we have a connection
    if ( $EMG -isnot "${NS}.EnterpriseManagementGroup" )
    {
        Throw "Could not connect to $ComputerName"
    }

    # Create some variables that we need for our script
    $DEFAULT = ("${NS}.Common.ObjectQueryOptions" -as "type")::Default
    $EMOT    = "${NS}.Common.EnterpriseManagementObject" -as "type"
    $IMGMT   = $emg.EntityObjects.GetType()
    $EMOP = "EnterpriseManagementObjectProjection"
    $IPT = "System.WorkItem.Incident.ProjectionType"
    ####

    # this function retrieves incidents from the Server. It uses reflection to
    # call the GetObjectProjectionReader method on the EntityObjects interface
    function Get-SMIncident
    {
        param ( $CRString )
        $Projection = $emg.EntityTypes.GetTypeProjections()|
            ?{$_.name -eq $IPT}
        if ( ! $Projection )
        {
            # FATAL ERROR
            throw "Could not retrieve projection type"
        }
        # Create the criteria which will allow us to retrieve the incident
        $CriteriaType = "${NS}.Common.ObjectProjectionCriteria"
        # This could be more strict, and the user can provide a SQL like Value
        # so you could use "IR17 %" to get IR17 or "IR%" to get all incidents
        # if you want more (or less) strictness, just change the criteria
        $CriteriaString = @’
<Criteria xmlns="http://Microsoft.EnterpriseManagement.Core.Criteria/"&gt;
  <Expression>
    <SimpleExpression>
      <ValueExpressionLeft>
        <GenericProperty>DisplayName</GenericProperty>
      </ValueExpressionLeft>
      <Operator>Like</Operator>
      <ValueExpressionRight>
        <Value>{0}</Value>
      </ValueExpressionRight>
    </SimpleExpression>
  </Expression>
</Criteria>
‘@
 -f $CRString

        try
        {
           $criteria = new-object $CriteriaType `
               $CriteriaString,$Projection,$Projection.ManagementGroup
           # use reflection to retrieve the incident by retrieving the
           # appropriate method and invoking it
           [type[]] $TYPES = ${CriteriaType},"${NS}.Common.ObjectQueryOptions"
           $ObjectReader = $IMGMT.GetMethod("GetObjectProjectionReader",$TYPES)
           $GenericMethod = $ObjectReader.MakeGenericMethod($EMOT)
           [array]$arguments = ($criteria -as "${CriteriaType}"),$DEFAULT
           # this will return the incident that matches the criteria
           ,$GenericMethod.invoke($emg.EntityObjects,$arguments)
        }
        catch
        {
            throw "Could not retrieve incidents"
        }
    }

    # this function takes an instance of an EnterpriseManagementObject and
    # creates a PS custom object. The custom object uses some of the properties
    # of the standard EnterpriseManagementObject but promotes the va
    function Get-AdaptedEMO
    {
        param ( $EMO )
        $Type = $EMO.GetType().Name
        $ClassName = $EMO.GetLeastDerivedNonAbstractClass().name
        $AdaptedObject = new-object psobject
        $AdaptedObject.PSObject.TypeNames.Insert(0,"${Type}#${ClassName}")
        # some standard properties that should be populated in our custom
        # object
        $TERMS = "LastModified","LastModifiedBy",
            "LeastDerivedNonAbstractManagementPackClassId"
        Add-Member -input $AdaptedObject NoteProperty Id $EMO.id.guid
        foreach($term in $TERMS)
        {
            Add-Member -input $AdaptedObject NoteProperty $Term $EMO.$Term
        }
        # get the content of values property and add them to our adapted object
        $EMO.Values | %{ 
            $AdaptedObject | Add-Member -force NoteProperty $_.Type $_.Value 
            } 
        $AdaptedObject
    }

} # END BEGIN

END
{
    # retrieve the incidents based on the incidentstring
    $IncidentCollection = Get-SMIncident $IncidentString
    # if we didn’t find an incident, provide a message and exit
    # one could argue that we should just exit
    if ( $IncidentCollection.Count -eq 0 )
    {
        $msg = "ERROR: No incidents match ‘$IncidentString’."
        Write-Host -fore red $b $msg
        Write-Host -fore red $b "Exiting"
        exit
    }
    # for each incident in the incidents that matched the criteria
    # construct a custom PSObject and emit
    foreach($Incident in $IncidentCollection)
    {
        # Create the custom object to hold the incident information
        $AdaptedIncident = new-object psobject
        $AdaptedIncident.PSObject.TypeNames.Insert(0,"${EMOP}#${IPT}")

        # Construct an adapted object for the main object of the incident
        $object = Get-AdaptedEMO $Incident.Object
        add-member -input $AdaptedIncident NoteProperty Object $object

        # for each one of the component parts of the incident, add an
        # adapted object to the adapted incident
        $keys = $Incident | %{ $_.key.name } | sort -uniq
        foreach($key in $keys )
        {
            Add-Member -Input $AdaptedIncident NoteProperty $key @()
            $Incident[$key] | %{ 
                $object = Get-AdaptedEMO $_.Object
                $AdaptedIncident.$key += $object
                }
        }

        # Add some members to the adapted incident
        # this will aid in presentation and filtering
        # first the simple promotions
        $Terms = "Id","Title","Description","DisplayName",
            "Priority","CreatedDate","LastModified"
        foreach($term in $Terms)
        {
            Add-Member -input $AdaptedIncident NoteProperty $term $AdaptedIncident.Object.$term
        }
        # more complex promotions
        Add-Member -input $AdaptedIncident NoteProperty Status $AdaptedIncident.Object.Status.DisplayName
        Add-Member -input $AdaptedIncident NoteProperty AssignedTo $AdaptedIncident.AssignedWorkItem[0].DisplayName
        Add-Member -input $AdaptedIncident NoteProperty AffectedUser $AdaptedIncident.RequestedWorkItem[0].DisplayName
        # emit the object
        $AdaptedIncident
    }
} # END

 

When we run the script, we provide an incident ID to reduce the amount of data returned from the Data Access Service.

PS> ./get-incident IR17%


Object                 : @{LastModified=1/28/2010 1:30:24 AM; LastModifiedBy=7431e155-3d9e-4724-895e-c03ba951a352; Leas
                         tDerivedNonAbstractManagementPackClassId=a604b942-4c7b-2fb2-28dc-61dc6f465c68; TargetResolutio
                         nTime=; Escalated=False; Source=IncidentSourceEnum.Console; Status=IncidentStatusEnum.Active;
                         ResolutionDescription=; NeedsKnowledgeArticle=False; TierQueue=; HasCreatedKnowledgeArticle=Fa
                         lse; LastModifiedSource=IncidentSourceEnum.Console; Classification=IncidentClassificationEnum.
                         Hardware; ResolutionCategory=; Priority=9; Impact=System.WorkItem.TroubleTicket.ImpactEnum.Hig
                         h; Urgency=System.WorkItem.TroubleTicket.UrgencyEnum.High; ClosedDate=; ResolvedDate=; Id=IR17
                         ; Title=my computer is brokeked; Description=it don't work; ContactMethod=; CreatedDate=1/18/2
                         010 10:58:23 PM; ScheduledStartDate=; ScheduledEndDate=; ActualStartDate=; ActualEndDate=; Dis
                         playName=IR17 - my computer is brokeked}
AppliesToTroubleTicket : {@{LastModified=1/28/2010 1:02:44 AM; LastModifiedBy=7431e155-3d9e-4724-895e-c03ba951a352; Lea
                         stDerivedNonAbstractManagementPackClassId=dbb6a632-0a7e-cef8-1fc9-405d5cd4d911; ActionType=Sys
                         tem.WorkItem.ActionLogEnum.RecordReopened; Title=Action log from 01/27/2010 17:02:44; Descript
                         setStatus=; Notes=; DisplayName=Domain Admninistrator}}
. . .
RequestedWorkItem      : {@{Id=642feed0-7e9a-b516-81cc-7f94be6bce91; LastModified=11/18/2009 5:30:17 PM; LastModifiedBy
                         =8bb08d83-64f1-4230-a8c9-e022beae2819; LeastDerivedNonAbstractManagementPackClassId=eca3c52a-f
                         273-5cdc-f165-3eb95a2b26cf; Domain=WOODGROVE; UserName=blesh; DistinguishedName=CN=Bruce Lesh,
                         CN=Users,DC=woodgrove,DC=com; SID=S-1-5-21-2548544548-3952215810-4123597018-1129; FQDN=woodgro
                         ve.com; UPN=blesh@woodgrove.com; FirstName=Bruce; Initials=; LastName=Lesh; Company=; Departme
                         nt=; Office=; Title=; EmployeeId=; StreetAddress=; City=; State=; Zip=; Country=; BusinessPhon
                         e=; BusinessPhone2=; HomePhone=; HomePhone2=; Fax=; Mobile=; Pager=; ObjectStatus=System.Confi
                         gItem.ObjectStatusEnum.Active; AssetStatus=; Notes=; DisplayName=Bruce Lesh}}
Id                     : IR17
Title                  : my computer is brokeked
Description            : it don't work
DisplayName            : IR17 - my computer is brokeked
Priority               : 9
CreatedDate            : 1/18/2010 10:58:23 PM
LastModified           : 1/28/2010 1:30:24 AM
Status                 : Active
AssignedTo             : Domain Admninistrator
AffectedUser           : Bruce Lesh

These results look pretty bad, it’s difficult to see what’s really important and what isn’t. However we can create a table view which can mimic the view that we see in the console. Here’s the formatting does it.

. . .
    <View>
      <Name>IncidentView</Name>
      <ViewSelectedBy>
        <TypeName>EnterpriseManagementObjectProjection#System.WorkItem.Incident.ProjectionType</TypeName>
      </ViewSelectedBy>
      <TableControl>
        <AutoSize />
        <TableHeaders>
          <TableColumnHeader><Label>Id</Label></TableColumnHeader>
          <TableColumnHeader><Label>Title</Label></TableColumnHeader>
          <TableColumnHeader><Label>AssignedTo</Label></TableColumnHeader>
          <TableColumnHeader><Label>Status</Label></TableColumnHeader>
          <TableColumnHeader><Label>Priority</Label><Alignment>Right</Alignment></TableColumnHeader>
          <TableColumnHeader><Label>AffectedUser</Label></TableColumnHeader>
          <TableColumnHeader><Label>LastModified</Label></TableColumnHeader>
        </TableHeaders>
        <TableRowEntries>
          <TableRowEntry>
           <TableColumnItems>
              <TableColumnItem><PropertyName>Id</PropertyName></TableColumnItem>
              <TableColumnItem><PropertyName>Title</PropertyName></TableColumnItem>
              <TableColumnItem><PropertyName>AssignedTo</PropertyName></TableColumnItem>
              <TableColumnItem><PropertyName>Status</PropertyName></TableColumnItem>
              <TableColumnItem><PropertyName>Priority</PropertyName></TableColumnItem>
              <TableColumnItem><PropertyName>AffectedUser</PropertyName></TableColumnItem>
              <TableColumnItem><PropertyName>LastModified</PropertyName></TableColumnItem>
           </TableColumnItems>
          </TableRowEntry>
        </TableRowEntries>
      </TableControl>
    </View>
. . .

update the format directives with update-formatdata, and voila!

PS> update-formatdata GetIncident.format.ps1xml
PS> ./get-incident IR17%

Id   Title                   AssignedTo            Status Priority AffectedUser LastModified
--   -----                   ----------            ------ -------- ------------ ------------
IR17 my computer is brokeked Domain Admninistrator Active        9 Bruce Lesh   1/28/2010 1:30:24 AM


PS> ./get-incident IR%

Id   Title                   AssignedTo            Status Priority AffectedUser  LastModified
--   -----                   ----------            ------ -------- ------------  ------------
IR17 my computer is brokeked Domain Admninistrator Active        9 Bruce Lesh    1/28/2010 1:30:24 AM
IR2  email is brokoken       Domain Admninistrator Closed        9 Al Young      12/15/2009 11:41:57 PM
IR13 more busted 2           Domain Admninistrator Closed        9 Carlos Garcia 1/28/2010 12:55:28 AM
IR15 busted 3                Domain Admninistrator Closed        9 Greg Adams    1/5/2010 6:50:11 PM

that looks pretty good!

Advertisements

Posted February 5, 2010 by jtruher3 in ServiceManager