We’ve arrived. We’re here. This is it! It’s time show how relating concepts in your organization to each other can provide you with environmental insights and provoke meaningful discussions. This is the ability to manage everything from data center to employees through System Center. A single database that details your entire environment – the ultimate PowerShell data source!

Getting Started with Relationships

Up until this point, we’ve focused a lot around single classes and their respective objects. For example:

  • 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

And so on, and so on. These examples are effective in their silos, but they only reveal surface deep properties about these items such as IP Address, Department, Title, DisplayName, and other properties on these items. So why can’t we see the Affected User of IR1234 or the Primary Owner of LPTP42356? Why not even just their username? Just something to give us a hint?

While relationships may make Operations Manager, Configuration Manager, and Service Manager feel more complicated, part of their brilliance in design is that they can easily map to real world tangible concepts.

  • Married? You and your partner are separate individuals, but known to each other through the “marriage” relationship.
  • Have a dog? You and your dog could be known through the “Owner has Pet” relationship. Since the name of this relationship is vague, it would be suitable if you had a fish or a cat as well.
  • Have a list of favorite movies? Maybe that’s the “Favorite Films” relationship that exists between a person and many pieces of cinema.

These examples help further highlight the nature of relationships in Service Manager. Distinct items each with their own characteristics that relate to one another. They also highlight another aspect of a basic data concept – one-to-one and one-to-many relationships. 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 SCSM Incident has one SCOM Alert or Azure ITSM Alert for which it was created

Enough talk. Let’s get those concepts with our next definitive command: Get-SCSMRelationshipObject

Get the Affected User of a Given Incident

$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"

Run this against an Incident of your choice and you should return a fair amount of Relationship Objects. Emphasis on Relationship Objects, not just single Objects, but pairs of Objects. Relationship Objects have a Source Object, Target Object, and the Relationship that binds them together. More simply put – if we’re starting with an Incident (source) what are objects on the other side of the Incident relationships? What types of objects in our environment does this Incident target? For starters, an Incident generally targets:

  • The Affected User relationship (i.e. User class)
  • The Created By User relationship (i.e. User class)
  • The Attached Files (i.e. File Attachment class)
  • The Related Work Items (i.e. Work Item class)
  • The Impacted Configuration Items (i.e. Configuration Item class)
  • The 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 – no one is expecting you 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! It’s actually called System.WorkItemAffectedUser. That answers that! 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 and say something to the effect of “Of all of the Related Objects on this Incident, get me the one whose Relationship ID equals the Relationship ID for the Affected User relationship class.” Now we see a single Relationship Object returned – the Incident and the Affected User. But what if we want just the single User object? As opposed to 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 (again – two objects) into a variable, then on the following line we make a call back to Get-SCSMObject. Only this time instead of having to use a class and a filter, we can just feed the command the unique identifier and let Service Manager do all 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
It’s another algebra style callback in the above example with an emphasis on the order of operations. We get all of the related objects, we then get only one by the Affected User relationship ID, we then 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.
Two different ways of accomplishing the exact same goal. What’s easier to read? What makes more sense to you? Rest easy knowing that whichever answer you arrive at is the right one.

 

Get the Incidents for a Particular User

Let’s run with the above example but in reverse. Let’s start with a User and get all of their 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
It’s another algebra callback in the above example with an emphasis on the order of operations (deja vu!). We get all of the related objects, we then get only one by the Affected User relationship ID, showing only 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. Just like like how you can guess classes with Get-SCSMClass, there is no penalty for guessing the wrong side of the relationship here. -BySource didn’t work? Then it must be -ByTarget and vice versa.

 

Because Incidents and Service Requests both have Affected Users, we get all of those results back from the above. How can we filter to just get the Incidents? Two ways:

First with 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
    }
}

 

Second with 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. Hrmmmm…

 

Follow Up via Email with an Affected User (About Incidents that are Pending and 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 just slightly a little bit more happening here, but it is a lot of what you’ve already seen. Get the classes we need to work with, get the objects we need use, build some variables for quick use later, throw them into a foreach loop so we can process them one by one, and finally use PowerShell’s native Send-MailMessage command to email our Affected User about their aging Pending Incident. Really the only new things in play here are using an environment variable ($env:username to retrieve the current user’s username), an IF statement within our foreach loop and using an HTML based email in PowerShell. Like all Powershell, these new parts can be tested independently of our larger script, which means you have the ability to iteratively create this solution. Test send email, substitute values. Test looping, substitute values, etc. etc. I want to emphasize this because it’s important to recognize that you don’t need to necessarily finish a script in a single sitting.

Think about the other possibilities here beyond just Incidents have been Pending longer than 3 days.

  • Change Requests from the DevOps Support Group with an In-Progress Status longer than 15 days
  • Problems created by Support with an Active Status more than 30 days without a Workaround and more than 40 related Incidents
  • Notifications to Custodians of Assets whose Warranty expires in 14 days
  • 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 – Any class in Service Manager. Any process in your organization. Automated.

7 Commands down. 1 to go.

 

Creating 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 within 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

 

It’s with New-SCSMRelationshipObject we’re able to define and shape data even further in our environment. We define our source object, our target object, and then the relationship that binds them together.

Think you could turn that into a function? A function that takes the input of a server name, username, Title, and Description? That way you can write once and run many? Just think of the number of processes you can build, turn into a function, and operationalize! Think of all of the other PowerShell modules you can leverage spanning Operations Manager, Configuration Manager, Active Directory, Azure, File Permissions and Exchange Online. Think how when you have all of System Center + Azure running, you can get all of your data from a single data source with just 8 commands.

Edit anything, create anything, and find everything. Which brings us to the…

Final Exam

  • Is Service Manager running with SCOM and SCCM connectors to help 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:
    • Their respective Rest APIs and PowerShell
      • Invoke-RestMethod + New-SCSMObject
    • Cireson Asset Import Connectors (SQL, LDAP, ODBC, or CSV)
    • SCOM Management Packs

 

This completes our series on Definitive Service Manager PowerShell spanning the only 8 commands you need to know to automate all of the things! But if you’re were hoping for just one more post that covered more  PowerShell Functions that you could use and immediately begin to put to use in your own environments – you’re in luck! We’ll see you next week for the last post in this series!

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