How to Work with Relationships in PowerShell

Cireson: working with relationships and PowerShell in SCSM

Following setup in parts 1-5 of this PowerShell series, Cireson Product Manager Adam Dzyacky ties everything together in this blog, with an emphasis on relationships and extracting meaning from data.

Definitive Service Manager PowerShell Blog Series, Part 6

Microsoft System Center Service Manager (SCSM) contains a great deal of data about your environment, but you may not know how to unlock or apply it. We’re going to walk through how to do this using PowerShell and relationships.

Previously, we looked at single classes and corresponding objects, including:

  • Incident class and IR1234
  • Hardware Asset class and LPTP42356
  • Active Directory User class and Bob in marketing
  • Active Directory Printer class and PR9873
  • Active Directory Computer and SCSMmgmt01

Each are individually effective, but they give us limited visibility into items like IP address, department, title, display name and other properties. What if you need to see the “affected user” or even the username of IR1234 or the primary owner of LPTP42356?

Data alone may or not be meaningful; often it’s the act of examining connections and interactions between concepts that help you gather environmental insights. And once you have those insights, you’re in a better position to have meaningful conversations. That’s why we’re going to start with relationships.

Relationships 101

Relationships in SCSM are not unlike personal relationships. For example, two individuals comprise a married couple. You are an individual, but you may be recognized as a pet owner and known through an owner / pet relationship. You may love film and have an association with the motion picture industry, preservation efforts or your local theatre. Relationships are how we’re identified and connected.

It’s the same thing in Service Manager. Relationships may seem complicated in Operations Manager, Configuration Manager and Service Manager, but they enable mapping to concrete concepts. And you could see a distinct item related to another or many. For example:

  • One manager has many direct reports
  • One employee has one manager
  • One service request has one “affected user”
  • One “incident” has many “file attachments”
  • One “incident” triggers one SCOM or Azure ITSM alert

Now looking specifically at your environment, what if you were asked, “How many ‘incidents’ came in today?” You could respond with the direct answer of 57. Is that number good, bad or better than yesterday? Well, let’s re-phrase the question:

“How many ‘incidents’ came in today in from the accounting department?” You could answer 23 out of the 57 total created today. This is based on the history of 23 incidents being created from employees in accounting. But is that good or bad? Is the accounting department having more or fewer issues? Let’s re-phrase the question one more time:

“How many accounting-related incidents came in today about an unavailable accounting application?”

You could answer 3 out of the 57 total created today.

You may be unaware of such conversations, or you might not be asked such questions. One thing is certain: conversation will not happen without supporting data.

There is potentially a lot of back-and-forth about the state of an environment. It’s hard to provide answers without data and knowledge of how it relates to other data. That said, let’s talk about common Service Manager relationships and specifically what may be the most common relationship seen on an incident or service request: the “affected user.”

We can get those concepts with our next definitive command: Get-SCSMRelationshipObject.

How to Find the Affected User of a Given Incident

Run this against an “incident” of your choice and you should return a fair amount of “relationship objects.” The emphasis is on “relationship objects,” meaning pairs of objects instead of single objects.

$irClass = Get-SCSMClass -name “System.WorkItem.Incident$” -computername “mgmtserver01”
$incident = Get-SCSMobject -class $irClass -filter “Name -eq ‘IR1235′” -computername “mgmtserver01”
Get-SCSMRelationshipObject -BySource $incident -computername “mgmtserver01”

“Relationship objects” have a “source object” and “target object.” The relationship binds them together. More simply put: if we start with an “incident” (source), what objects are on the other side of the “incident” relationship? What types of objects in our environment does this “incident” target? For starters, an “incident” generally targets:

  • “Affected user” relationship (i.e. user class)
  • “Created by user” relationship (i.e. user class)
  • “Attached files” (i.e. “file attachment” class)
  • “Related work items” (i.e. “work item” class)
  • “Impacted configuration items” (i.e. “configuration item” class)
  • “Related configuration items” (i.e. “configuration item” class)

Given the list of “relationship objects” returned, you may spend an unusual amount of time trying to guess which relationship is which. Don’t worry, you’re not expected to memorize relationship GUIDs so let’s make this a bit easier to manage with our next definitive command: Get-SCSMRelationshipClass

$irClass = Get-SCSMClass -name “System.WorkItem.Incident$” -computername “mgmtserver01”
$affectedUserRelClass = Get-SCSMRelationshipClass -Name “System.WorkItemAffectedUser$” -computername “mgmtserver01”
$incident = Get-SCSMobject -class $irClass -filter “Name -eq ‘IR1235′” -computername “mgmtserver01”
Get-SCSMRelationshipObject -BySource $incident -computername “mgmtserver01” | Where-Object {$_.RelationshipId -eq $affectedUserRelClass.Id}

Here we’re using the Get-SCSMRelationshipClass command to get a “relationship class” (just like getting a normal stand-alone class). Since I didn’t know what it was called, just like Get-SCSMClass, this works as an implicit wild card.

Get-SCSMRelationshipClass -Name “affected” -computername “mgmtserver01”

AH! Now we have our answer–it’s actually called: System.WorkItemAffectedUser. We’ll throw it into a variable to use later. Then when we use: Get-SCSMRelationshipObject, which produces all of the related objects from the “incident,” we can use the PowerShell pipeline. We can set something like this in motion:

  • On this “incident,” isolate the “related object” with a “relationship ID” that equals the “relationship ID” for the “affected user” relationship class.
  • What is returned: a single “relationship object,” comprising an “incident” and “affected user.”

What if we want to isolate the “single user object” instead of receiving the “relationship object” of the “incident” and the “affected user?”

$irClass = Get-SCSMClass -name “System.WorkItem.Incident$” -computername “mgmtserver01”
$affectedUserRelClass = Get-SCSMRelationshipClass -Name “System.WorkItemAffectedUser$” -computername “mgmtserver01”
$incident = Get-SCSMobject -class $irClass -filter “Name -eq ‘IR1235′” -computername “mgmtserver01”
$incidentAURelObject = Get-SCSMRelationshipObject -BySource $incident -computername “mgmtserver01” | Where-Object {$_.RelationshipId -eq $affectedUserRelClass.Id}
Get-SCSMObject -id $incidentAURelObject.TargetObject.Id -computername “mgmtserver01”

We throw our “relationship object” (reminder: this is two objects) into a variable, then on the following line we make a call back to Get-SCSMObject. Only this time instead of using a class and a filter, we can just feed the command the unique identifier and let Service Manager do the work for us. But you could also write it like this if you prefer:

$irClass = Get-SCSMClass -name “System.WorkItem.Incident$” -computername “mgmtserver01”
$affectedUserRelClass = Get-SCSMRelationshipClass -Name “System.WorkItemAffectedUser$” -computername “mgmtserver01”
$incident = Get-SCSMobject -class $irClass -filter “Name -eq ‘IR1235′” -computername “mgmtserver01”
Get-SCSMObject -id (Get-SCSMRelationshipObject -BySource $incident | Where-Object {$_.RelationshipId -eq $affectedUserRelClass.Id}).TargetObject.Id

The above algebra-style callback emphasizes the order of operations:

  • We receive all the “related objects,” and then get one “affected user relationship ID.”
  • We show only the ID property of the target of the relationship (the user in this case) and then feed that to the Get-SCSMObject command.

They represent two different ways of accomplishing the exact same goal. What’s easier to read? What makes more sense to you? Whatever your answer, it is the right one.

How to Get User Incidents

Let’s reverse the above example, starting with a user, and get all user “incidents.”

$environment = “mgmtserver01”
$userClass = Get-SCSMClass -Name “System.Domain.User$” -computername $environment
$affectedUserRelClass = Get-SCSMRelationshipClass -Name “System.WorkItemAffectedUser$” -computername $environment
$user = Get-SCSMObject -class $userClass -filter “Username -eq ‘adam'” -computername $environment
(Get-SCSMRelationshipObject -ByTarget $user -computername $environment | Where-Object {$_.RelationshipId -eq $affectedUserRelClass.Id}).SourceObject

The above example is similar to the last one: it’s also an algebra callback that emphasizes the order of operations. We see all “related objects,” then we get one by the “affected user relationship ID,” which shows the source objects (“work items” in this case). In this and the previous example, notice how the -BySource and -ByTarget parameters are switched depending on how we’re approaching the Service Manager object in question. Similar to the way that you can guess classes with Get-SCSMClass, there is no penalty for guessing the wrong side of the relationship here. If -BySource didn’t work, then -ByTarget must be correct, and vice versa.

Filter to See Incidents

 “Incidents” and “service requests” both have “affected users.” You can see in the above example how we get those results back. But how can we filter to see the “incidents?” Two ways:

 1. Use a Foreach Loop

$userClass = Get-SCSMClass -name “System.Domain.User$” -computername “mgmtserver01”

$affectedUserRelClass = Get-SCSMRelationshipClass -Name “System.WorkItemAffectedUser$” -computername “mgmtserver01”

$user = Get-SCSMobject -class $userClass -filter “Username -eq ‘adam'” -computername “mgmtserver01”

$workItems = (Get-SCSMRelationshipObject -ByTarget $user -computername “mgmtserver01” | Where-Object {$_.RelationshipId -eq $affectedUserRelClass.Id}).SourceObject

foreach ($workitem in $workItems)

{

    if ($workitem.ClassName -eq “System.WorkItem.Incident”)

    {

        #show the work item on the PowerShell command line

        $workitem

    }

}

2. Use the Pipeline

$userClass = Get-SCSMClass -name “system.domain.user$” -computername “mgmtserver01”
$affectedUserRelClass = Get-SCSMRelationshipClass -Name “System.WorkItemAffectedUser$” -computername “mgmtserver01”
$user = Get-SCSMobject -class $userClass -filter “Username -eq ‘adam'” -computername “mgmtserver01”
(Get-SCSMRelationshipObject -ByTarget $user -computername “mgmtserver01” | Where-Object {$_.RelationshipId -eq $affectedUserRelClass.Id}).SourceObject | Where-Object {$_.ClassName -eq “System.WorkItem.Incident”}

Both produce the identical result. But the foreach loop opens the door to some interesting possibilities….

Follow Up with the Affected User via Email

Try this for pending “incidents” that haven’t been updated in more than 3 days:

$environment = “scsmdevmgmt01”

$emailServerFQDN = “mail.lab.lcl”

$userClass = Get-SCSMClass -name “System.Domain.User$” -computername $environment

$affectedUserRelClass = Get-SCSMRelationshipClass -Name “System.WorkItemAffectedUser$” -computername $environment

$user = Get-SCSMObject -class $userClass -filter “Username -eq ‘adam'” -computername $environment

$me = Get-SCSMObject -class $userClass -filter “Username -eq ‘$env:username'” -computername $environment

$usersFirstName = $user.FirstName

$usersWorkItems = (Get-SCSMRelationshipObject -ByTarget $user  -computername $environment | Where-Object {$_.RelationshipId -eq $affectedUserRelClass.Id}).SourceObject | Where-Object {$_.ClassName -eq “System.WorkItem.Incident”}

foreach ($workitem in $usersWorkItems)

{

    $currentWorkItem = Get-SCSMObject -id $workitem.id -computername $environment

    $currentWorkItemId = $currentworkitem.id

    $currentWorkItemTitle = $currentWorkItem.Title

    if (($currentWorkItem.Status.DisplayName -eq “Pending”) -and ($currentWorkItem.LastModified -lt (Get-Date).AddDays(-3)))

    {

        $body = “Hey there $usersFirstName, <br />

        I just wanted to follow up with you on $currentWorkItemTitle. If you’re free today or later this week, maybe we could schedule some time to troubleshoot?

        <br/><br />

        thanks, <br />

        Adam”

        Send-MailMessage -from $me.UPN -to $user.UPN -Subject “Follow up on [$currentWorkItemId]” -Body $body -SmtpServer $emailServerFQDN

    }

}

Now there is a little bit more happening here, but much of it you’ve already seen:

  • Get the classes you need to work with
  • Get the objects you need use
  • Build variables for quick use later
  • Throw variables into a foreach loop so you can process them one by one
  • Use PowerShell’s native Send-MailMessage command to email our “affected user” about their aging pending incident

The new things in play here are:

  • Using an environment variable ($env:username to retrieve the current user’s username)
  • An IF statement in our foreach loop
  • Using an HTML-based email in PowerShell

Like all PowerShell activity, these new parts can be tested independently of our larger script, which means you can iteratively create this solution. Try test sending email, substituting values, test looping, substitute values, etc. It’s important to recognize that you don’t need to finish a script in a single sitting. You can work in increments.

You can apply this process to a number of other scenarios. Think about possibilities beyond “incidents” pending for more than three days, including:

  • “Change requests” from the DevOps Support Group with an in-progress status extending beyond 15 days
  • Problems created by support with more than 30-day active status without a workaround and more than 40 related “incidents”
  • Notifications to asset custodians with pending warranty expirations
  • Notify the “change request” “created by user” 48 hours before a change meeting that the change is missing “affected configuration items”
    • Go one step further with a second script that cancels “change requests” missing “configuration items”
  • Cancel “service requests” that have a “review activity” in-progress longer than 45 days

The possibilities here are truly endless: automate any class in Service Manager with any process in your organization!

 The Last Command: Create an Incident with an Affected User and Impacted Service

Time to cover the very last command in the definitive Service Manager PowerShell series: New-SCSMRelationshipObject. With this command we can further shape items in Service Manager. Let’s create an “incident” again but fill out the relationships.

#Get everything we need to work with
$environment = “dev”
$irClass = Get-SCSMClass -Name “System.WorkItem.Incident$” -computername $environment
$userClass = Get-SCSMClass -Name “System.Domain.User$” -computername $environment
$computerClass = Get-SCSMClass -Name “Microsoft.Windows.Computer$” -computername $environment
$server = Get-SCSMObject -class $computerclass -filter “PrincipalName -eq ‘sqlServer72.lab.lcl'” -computername $environment
$affectedUserRelClass = Get-SCSMRelationshipClass -Name “System.WorkItemAffectedUser$” -computername $environment
$createdByUserRelClass = Get-SCSMRelationshipClass -name “System.WorkItemCreatedByUser$” -computername $environment
$wiAboutCIRelClass = Get-SCSMRelationshipClass -name “System.WorkItemAboutConfigItem$” -computername $environment
$user = Get-SCSMObject -class $userClass -filter “Username -eq ‘adam'” -computername $environment
$me = Get-SCSMObject -class $userClass -filter “Username -eq ‘$env:username'” -computername $environment
$wiAboutCIRelClass = Get-SCSMRelationshipClass -name “System.WorkItemAboutConfigItem$” -computername $environment

#define the Incident
$incidentProperties = @{
     “ID” = “IR{0}”;
     “Status” = “IncidentStatusEnum.Active$”;
     “Title” = “Computer is burning”;
     “Description” = “It seems to be on fire.”;
     “Classification” = $null;
     “Impact” = “System.WorkItem.TroubleTicket.ImpactEnum.High$”;
     “Urgency” = “System.WorkItem.TroubleTicket.UrgencyEnum.High$”;
     “Source” = “IncidentSourceEnum.Console$”;
     “TierQueue” = “37efa6f4-b120-420b-b1e0-f15abed95d12”
}

#create the Incident
$newIncident = New-SCSMObject -Class $irClass -PropertyHashtable $incidentProperties -computername $environment -PassThru 

#set the Created By User to the user running this script
New-SCSMRelationshipObject –Source $newIncident –Relationship $createdByUserRelClass –Target $me -computername $environment -Bulk
#set the Affected User
New-SCSMRelationshipObject –Source $newIncident –Relationship $affectedUserRelClass –Target $user -computername $environment -Bulk
#set the Server as the Impacted Service on the Incident
New-SCSMRelationshipObject –Source $newIncident –Relationship $wiAboutCIRelClass –Target $server -computername $environment -Bulk

With New-SCSMRelationshipObject, we can define and shape data even further in our environments. We can define our source object, our target object and the relationship that binds them together.

You could turn that into a function that takes the input of a server name, username, title and description, allowing you to write once and run many. Just think of the number of processes you can build, turn into a function and operationalize! Think of all the other PowerShell modules you can use, including Operations Manager, Configuration Manager, Active Directory, Azure, File Permissions and Exchange Online. With System Center and Azure running, you can access all of your data from a single data source using eight commands.

You have the ability to edit anything, create anything and find everything. Which brings us to the:

 Final Checklist

  • Is Service Manager running with SCOM and SCCM connectors to round out your data?
    • Configuration Manager can populate “primary users to assets,” and software updates on computer inventory
    • Operations Manager can populate monitored inventory in your environment
      • Monitoring Azure with SCOM? You can sync Azure inventory into Service Manager by importing the Azure MP into SCSM
      • Discovered computer inventory from SCOM can add IP addresses into machines
      • Monitored network devices can make their way into Service Manager by importing the network monitoring MP into SCSM
  • What data would you want to store in Service Manager that exists in other systems? You could sync data from external sources with:
    • Respective rest APIs and PowerShell
      • Invoke-RestMethod + New-SCSMObject
    • Cireson asset import connectors (SQL, LDAP, ODBC, or CSV)
    • SCOM management packs

This completes our Definitive Service Manager PowerShell blog series. Hopefully you have confidence in the eight commands needed to automate all of the things! But if you were hoping for a little more guidance on PowerShell functions, you’re in luck. Look for the last post in this series.

Definitive Service Manager PowerShell Blog Series: Part 1 / Part 2 / Part 3 / Part 4 / Part 5

*This blog has been updated from its original 2020 version, Definitive Service Manager PowerShell: Part 6 – Working with Relationships.

Experience Teams Ticketing Today

Start your 14-day free trial of Tikit. No credit card required.