Auto Assign SCSM Incidents with Azure Automation
Have you ever wanted Service Manager to auto assign Incidents to analysts? This blog talks through doing just that!
Rather than relying on engineers manually picking up incidents, or have someone assigning them, this solution will automatically assign out incidents, up to a set limit.
You can easily schedule this script to run, using Azure Automation. Using the Hybrid Worker feature, this will run on-premises, against your existing SCSM environment. No changes or Management Packs required!
Here’s a walkthrough of what happens:
- New incidents are waiting in the Tier 1 Support Group
- This script runs & looks up who is in an AD Group for the Tier 1 Support Group
- We then look at how many incidents are assigned to each Tier 1 analyst, to see who has the least work
- The incident is assigned to the analyst with the fewest assigned active incidents
- If >1 Analyst have the fewest number of active incidents, it’s assigned to whoever has gone longest since last having an incident assigned
- You can configure a maximum Incident assignment limit, e.g. 10 active incidents per analyst. If all analysts are full, then incidents wait in the Tier 1 Support Group
- Optionally, you can enable ‘Auto Escalation’. If all Tier 1 analysts are at capacity & there are more unassigned active Tier 1 Incidents, they can be automatically bumped to another support Group, e.g. Tier 2.
Step 1: Register a Hybrid Worker
If you haven’t already, make sure you have a Hybrid Worker role installed on-premises, for your Azure Automation account. This is super easy & documented at https://azure.microsoft.com/en-us/documentation/articles/automation-hybrid-runbook-worker/.
After you’ve done this, you should see your on-prem worker listed under the ‘Hybrid Worker Groups’ tile in your Azure Automation account:
Step 2: PowerShell modules on the Hybrid Worker
Your Hybrid Worker on-prem, is going to be running this PowerShell script. It requires the Service Manager & Active Directory PowerShell modules.
Simply install the Service Manager console, on the Hybrid Worker. This will install the SCSM PowerShell module.
For the AD PowerShell module, open up an Administrative Windows PowerShell & run:
Add-WindowsFeature RSAT-AD-PowerShell
Step 3: Configure SCSM Credentials
Go to your Azure Automation Account > Assets > Credentials
Add in the credentials of an account with access to Service Manager. This is who the script will be run as.
Here, you can see my account is named contoso-rob
Step 4: Create + Configure a new PowerShell Runbook
In your Azure Automation Account, create a new PowerShell based Runbook. Paste in the PowerShell script which I've placed at the bottom of this blog.
You’ll want to make a few configuration changes to the script.
Line 25: I’m calling my stored credentials. Change contoso-rob to whatever your SCSM account display Name is, which you made in step 3.
Line 29: This is the support Group to monitor for new Incidents to be assigned. This is the display name of the item in the Support Group list on the Incident form.
Line 31: If you enable auto-escalation, this is the support group the Incidents will be moved to.
Line 33: The AD Group which defines the Analysis for your support group. These are the people who will have the incidents automatically assigned to them. Please note – by default, this recursively gets the AD members. If you want to switch the recursion off, see line 97 (Get-ADGroupMember).
Line 35: The maximum number of active Incidents per analyst.
Line 37: Set to $true if you want the auto-escalation feature enabled.
That’s it – Save & publish your Runbook!
Step 5: Give it a whirl!
Run the Runbook from the Azure Automation account to test it out. When you click run, it’ll ask you for the name of a SCSM Management server. (Hint: You could change this to store it in an Automation Asset!). Remember, this is an on-prem server, which the Hybrid Worker will communicate to.
Make sure you select your Hybrid worker, to run it on prem!
After it’s run, you can have a look at the Job output. This shows the history of which Incidents were assigned, who to & any escalation, or if all analysts are at capacity.
Step 6: Schedule it!
If you’re happy how the Runbook ran, you can schedule it to run regularly.
In Azure Automation, go to the Runbook > Schedules > Add a Schedule. Set this to whatever you like – I put mine as hourly.
Be sure to set the parameter, with the name of your SCSM Management Server & to Run on the hybrid worker.
#######################################################################################################################
# Assigns active incidents for a particular Support Group to the members of an AD Group
# Author: Rob Davies (Microsoft)
# Disclamer: This program source code is provided "AS IS" without warranty representation or condition of any kind
# either express or implied, including but not limited to conditions or other terms of merchantability and/or
# fitness for a particular purpose. The user assumes the entire risk as to the accuracy and the use of this
# program code.
# Tested with System Center 2012 R2 Service Manager with PowerShell v4
# Name: Assign-SCSMIncidents.ps1
# v1.0 - 7/3/2015 - Initial Release
# v1.1 - 7/12/2016 - Updates for Azure Automation
#######################################################################################################################param (
[string]$SCSMManagementServer="localhost"
)if (!(Get-module System.Center.Service.Manager))
{
$scsmRegKey = 'HKLM:\SOFTWARE\Microsoft\System Center\2010\Service Manager\Setup'
Import-Module "$((Get-ItemProperty -Path $scsmRegKey -Name InstallDirectory).InstallDirectory)\PowerShell\System.Center.Service.Manager.psd1" -ErrorAction Stop
}
if (!(Get-module ActiveDirectory)) {import-module ActiveDirectory -ErrorAction Stop }$smCreds = Get-AutomationPSCredential -Name 'contoso-rob'
#============================================================================
#TODO: Set supportGroup as the DISPLAY NAME of the Support Group to monitor
$supportGroup = "Tier 1"
#TODO: Set escalationGroup as the DISPLAY NAME of the support group to escalate to when the supportGroup is full
$escalationGroup = "Tier 2"
#TODO: Distinguished Name of AD Group and find members:
$ADGroupName = "CN=Tier1,OU=Workshop,DC=contoso,DC=work"
#TODO: Max number of active incidents assigned to operator:
$maxAssignedIncidents = 7
#TODO: Auto-Escalate. If $True enables the ability to esclate to escalationGroup. Either: $True or $False
$autoEscalate = $False
#============================================================================#Work Item Assigned Relationship Type
$WIAssigned = Get-SCRelationship -Name "System.WorkItemAssignedToUser" -ComputerName $SCSMManagementServer -Credential $smCredsfunction whoToAssignTo ($helpdeskUserList) {
#Get the user with the fewest number of active incidents assigned
$numberOfAnalystsWithFewestIncidents=0
$firstUser = $helpdeskUserList | Sort-Object AssignedIncidents | Select-Object -First 1#Find out if there are any other users with the same number of active assigned incidents
foreach ($x in $helpdeskUserList) {
if ($x.AssignedIncidents -eq $firstUser.AssignedIncidents) {$numberOfAnalystsWithFewestIncidents++}
}
$availableUser = $helpdeskUserList | Sort-Object AssignedIncidents | Select-Object -First $numberOfAnalystsWithFewestIncidents#If there is >1 user with fewest number of assigned active Incidents
if ($availableUser.Count -gt 1){#Find the incidents which was last assigned for each user
$recentIncidents = @()
foreach ($x in $availableUser) {
$mostRecentIR = $x.ListOfIncidents | Sort-Object LastAssigned -Descending | select -First 1
$recentIncidents = $recentIncidents + $mostRecentIR
}#What if there are multiple users with 0 incidents
if($recentIncidents.Count -gt 0) {
#Find the incident which was assigned the longest time ago. Then find the user who is assigned to this
$mostRecentIRFilter = "Id -eq " + ($recentIncidents | Sort-Object LastAssigned | select -First 1).id
$mostRecentIR = Get-SCClassInstance -Class (Get-SCClass -Name System.WorkItem.Incident -ComputerName $SCSMManagementServer -Credential $smCreds) -Filter $mostRecentIRFilter -ComputerName $SCSMManagementServer -Credential $smCreds
$userLastAssignedAnIncident = Get-SCClassInstance -Id (Get-SCRelationshipInstance -SourceInstance $mostRecentIR -ComputerName $SCSMManagementServer -Credential $smCreds |?{$_.RelationshipId -eq $WIAssigned.Id -and $_.IsDeleted -eq $false}).TargetObject.Id -ComputerName $SCSMManagementServer -Credential $smCreds#return the user to assign the incident to
return $userLastAssignedAnIncident
} else {
#If multiple with 0 incidents assigned randomly pick someone
$randomUser = ($availableUser | Get-Random)
$userFilter = "DistinguishedName -eq " + $randomUser.UserDN
$userLastAssignedAnIncident = Get-SCClassInstance -Class (Get-SCClass -Name Microsoft.AD.User -ComputerName $SCSMManagementServer -Credential $smCreds) -Filter $userFilter -ComputerName $SCSMManagementServer -Credential $smCreds#return the user to assign the incident to
return $userLastAssignedAnIncident
}} elseif ($availableUser.Count -eq $null) {
#If there is only one user with lowest number of active incidents, then get the user object from SCSM
$userFilter = "DistinguishedName -eq " + $availableUser.UserDN
$userLastAssignedAnIncident = Get-SCClassInstance -Class (Get-SCClass -Name Microsoft.AD.User -ComputerName $SCSMManagementServer -Credential $smCreds) -Filter $userFilter -ComputerName $SCSMManagementServer -Credential $smCreds#return the user to assign the incident to
return $userLastAssignedAnIncident
}}
function populateHelpdeskUserList ($GroupName, $maxIncidentCount) {
#Get the members of the AD Group
#TODO: Currently set to Recursive. Remove -Recursive if direct membership is wanted
$usersGroup = Get-ADGroup -Identity $GroupName | Get-ADGroupMember -Recursive#List to store analysts and number of active incidents
$helpdeskUserList = @()#Calculate how many active Incidents assigned to each member
foreach ($usr in $usersGroup) {
$IRAssigned = 0#Find the user in SCSM
$userFilter = "UserName -eq " + $usr.SamAccountName
$userCI = Get-SCClassInstance -Class (Get-SCClass -Name System.Domain.User -ComputerName $SCSMManagementServer -Credential $smCreds) -Filter $userFilter -ComputerName $SCSMManagementServer -Credential $smCreds#Ensure the user is in SCSM
if($userCI -ne $null) {
#Get Active Incidents assigned to the user
$IDs = (((Get-SCRelationshipInstance -TargetInstance ($userCI) -ComputerName $SCSMManagementServer -Credential $smCreds | ?{$_.RelationshipId -eq $WIAssigned.Id -and $_.IsDeleted -eq $false}).SourceObject | ?{$_.FullName -like "System.WorkItem.Incident*"}).Id)#if user has >0 incidents find count of how many active
if ($IDs -ne $null) {
$assignedIncidents = Get-SCClassInstance -Id $IDs -ComputerName $SCSMManagementServer -Credential $smCreds | ?{$_.Status.Name -eq "IncidentStatusEnum.Active"}
$IRAssigned = $assignedIncidents.Count#If user not at capcity for incidents
if ($IRAssigned -lt $maxIncidentCount) {#Get the list of Active Incidents assigned to user and DateTime of when last assigned
$assignedIRToUser = @()
foreach($IR in $assignedIncidents) {
$assignedIR = New-Object PSObject -Property @{
Id = $IR.Id
LastAssigned = (Get-SCRelationshipInstance -SourceInstance $IR -ComputerName $SCSMManagementServer -Credential $smCreds |?{$_.RelationshipId -eq $WIAssigned.Id -and $_.IsDeleted -eq $false}).LastModified
}
$assignedIRToUser = $assignedIRToUser + $assignedIR
}#Add the Incident details for this user to the list
$helpdeskUser = New-Object PSObject -Property @{
UserName = $usr.SamAccountName
UserDN = $usr.distinguishedName
AssignedIncidents = $IRAssigned
ListOfIncidents = ($assignedIRToUser | Sort-Object LastAssigned -Descending)
}
#Add user to list of available engineers
$helpdeskUserList = $helpdeskUserList + $helpdeskUser
}
} else { #If the user has 0 incidents assigned
#Add User to the list with 0 assigned
$helpdeskUser = New-Object PSObject -Property @{
UserName = $usr.SamAccountName
UserDN = $usr.distinguishedName
AssignedIncidents = 0
ListOfIncidents = $null
}
#Add user to list of available engineers
$helpdeskUserList = $helpdeskUserList + $helpdeskUser
}
}
}
return $helpdeskUserList
}try {
Write-Output "Start Time: $(Get-Date -Format MM-dd-yyyy.HH:mm:ss)"#Get a list of all Active Incidents in the Support Group
$activeIncidentsInSupportGroup = Get-SCClassInstance -Class (Get-SCClass -Name System.WorkItem.Incident -ComputerName $SCSMManagementServer -Credential $smCreds) -Filter "Status -eq IncidentStatusEnum.Active" -ComputerName $SCSMManagementServer -Credential $smCreds | ?{$_.TierQueue.DisplayName -eq $supportGroup}
#Find assigned incidents, if there are any active incidents in support group
if ($activeIncidentsInSupportGroup -ne $null) {
$assignedIncidents = (Get-SCRelationshipInstance -SourceInstance ($activeIncidentsInSupportGroup) -ComputerName $SCSMManagementServer -Credential $smCreds | ?{$_.RelationshipId -eq $WIAssigned.Id -and $_.IsDeleted -eq $false}).SourceObject.Name
#Get a list of unassigned Incidents in the Support Group
$unassignedIncidents = $activeIncidentsInSupportGroup | where {$_.Id -notin $assignedIncidents}
}Write-Output "Incidents to assign $($unassignedIncidents.Count)"
#If there are Incidents to assign
if($unassignedIncidents.Count -gt 0) {#Loop through every unassigned incidents starting with the oldest by created date
foreach ($unassignedIncident in ($unassignedIncidents | Sort-Object CreatedDate)) {#Get the current list of users available to assign incidents to
$TheUserList = populateHelpdeskUserList $ADGroupName $maxAssignedIncidents
write-output $TheUserList#Get the user to assign this particular incident to
$userToAssignIR = whoToAssignTo $TheUserList#Make sure there is a user in the list to assign to
if ($userToAssignIR -ne $null){
New-SCRelationshipInstance -RelationshipClass $WIAssigned -Source $unassignedIncident -Target $userToAssignIR -ComputerName $SCSMManagementServer -Credential $smCreds
write-output "ASSIGNED: $($unassignedIncident.Id) to: $($userToAssignIR.DisplayName)"
} else {
#No available users to assign the incident to
write-output "FULL: No engineers available in $($supportGroup) to to assign incident $($unassignedIncident.Id)"
if ($autoEscalate) {
$unassignedIncident.TierQueue = $escalationGroup
$unassignedIncident | Update-SCClassInstance
write-output "ESCALATED: $($unassignedIncident.Id) to $($escalationGroup)"
} elseif (!$autoEscalate) {
Write-Output "Exiting script as assignment slots are full and auto escalation is disabled"
break
}
}
}
} else {
#No incidents needed to be assigned
write-output "Currently no incidents to assign"
}
}
catch {
Write-Warning "ERROR: Something bad happened!"
$_.Exception.Message
}