• Glen (Xapity)

PowerShell for SCSM: Bulk Update Classifications



Bulk updating Service Manager classifications, or for that matter any list value is sometimes required. The easiest way to do this is by using PowerShell and this blog post will outline some scripts that help achieve this.

Classification Design Considerations

But first, it is better to avoid having to make bulk changes. So here are some design considerations for list values, and in particular classifications. In the end it will depend on your organisation business practices, analysts and your reporting needs.

Classifications - less is usually better

Don't create a classification just because you think it might be needed. All list values should be used ie in reports or sometimes for views in the console.

It is easy for business areas to go overboard and have too many classifications that never get used. If you are not going to report on the classification, then there is not much point in getting analysts to set the value.

When there are too many choices, choices that are too similar or different hierarchy paths to similar choices, it is difficult for analysts to be consistent.

You can also have templates that are created by admins that use one value, but analysts logging jobs have, by word of mouth, used a different value. This splits the jobs across two different reporting values.

Check your existing classifications to see if any are not in use. You may be surprised at how analysts are using the values.

It is Easy to Add new Items

Adding new list values is easy and usually is done to add more detail to a higher level or for a new requirement. Analysts just have to start using the new value.

Occasionally, the new value will trigger a bulk update to move some jobs to the new value

And Easy to Rename

Renaming list values is also easy, as you are only changing the display name of the list enumeration (Enum). It does not actually change the Enum value that is stored in the databases by Service Manager.

Sometimes renaming a list value to a display name eg" System - Do Not Use - Original Value" (and move it to the bottom of the list), can be easier than deleting the value. This has the advantage that it will flow on to any groomed jobs in the data warehouse, as the original Enum is still in use.

But Removing List Values is more Complex

Try to avoid removing list values as this will not remove the data in list fields on existing or completed jobs (ie the Enums remain on the job). This can lead to reporting problems where the removed list value shows up with the Enum value rather than the display name.

Removing list values is one of the primary reasons that you may need to make bulk classification changes (and why you are reading this blog). It involves remapping the old list value to a new list value on all jobs in the operational database. You can then remove the list value.

Updating the groomed jobs in the data warehouse is out of scope for this blog post, as the PowerShell commands below will only work on the Operational database.

Enumerations

First we need to deal with Enumerations - the list values.

Each list value is stored as an Enumeration (Enum), this is essentially a flat structure in Service Manager. In the console when you see "List Item 1\List Item 2\List Item 3" but this is a derived value using the parent\child information on the Enum, only the final Enum value is stored in the field on the object.

To view the all Enums in Service Manager you can use:

Get-SCSMEnumeration |Sort Displayname | Select Displayname, Name

This will produce a long list of Enums and it is a bit overwhelming. It gives a list of the Enums, but with no Path information.

It is possible to target one Enum and get all related child Enums i.e. the hierarchy in the list from that point. From the list of all the Enums generated above you could target any specific Enum to see if there were any child Enums.

To get just the Incident classifications you can use the IncidentClassificationEnum as this is the overall parent Enum for the Classification list:

$rootClassificationEnum = Get-SCSMEnumeration -Name IncidentClassificationEnum$

$AllClassificationChildEnum = get-scsmchildenumeration -Enumeration $rootClassificationEnum

$AllClassificationChildEnum | Select DisplayName, Name

And an example for all Tier Queues:

$rootTierQueuesEnum = Get-SCSMEnumeration -Name TierQueuesEnum$

$AllTierQueueChildEnum = get-scsmchildenumeration -Enumeration $rootTierQueuesEnum

$AllTierQueueChildEnum | Select DisplayName, Name

Enumeration ID and Name

Where possible use the ID or Name value of the Enumeration as these are going to be unique. In the scripts below I have sometimes used display name as it is human friendly, but it might not be unique.

If you are doing a bulk updates , you need to be very sure you are updating the correct incidents.I would use the Enum Name or ID where possible just to be sure that you are targeting the correct Incidents.

Bulk Update Classifications: Scripts

These are a number of ways to do things with PowerShell and these scripts are what made sense to me. There maybe better ways or more efficient, but hopefully these will give you a good starting point.

Download the scripts from Technet Gallery here, as coping off this page might contain unwanted HTML tags.

Script 1: Get a List of Classification and Tier Queue Enums

This will produce a list on the screen of the Classification and Tier Queue Enums.

AllClassificationTierQueueEnums.ps1

###################################################################################

#

# Display all Incident Classification and Tier Queue Enumerations

#

###################################################################################

Write-Host

Write-Host "Incident Classification Enumerations"

Write-Host "=================================================================================="

$rootClassificationEnum = Get-SCSMEnumeration -Name IncidentClassificationEnum$

$AllClassificationChildEnum = get-scsmchildenumeration -Enumeration $rootClassificationEnum

$AllClassificationChildEnum | Select DisplayName, Name, ID

Write-Host

Write-Host "Incident Support Group (Tier Queue) Enumerations"

Write-Host "=================================================================================="

$rootTierQueuesEnum = Get-SCSMEnumeration -Name TierQueuesEnum$

$AllTierQueueChildEnum = get-scsmchildenumeration -Enumeration $rootTierQueuesEnum

$AllTierQueueChildEnum | Select DisplayName, Name, ID

Example Output:


Script 2: Enumeration Path of Enum

This script takes a Enum display name and gets the path for that Enum.

This allows you to check if a display name is unique and if not to see where it has been used. You could also modify this script to get the path for all Enums by taking the filter off the $MatchingEnums variable.

EnumPath.ps1

###################################################################################

#

# Gets the Path of any Enum from user input of list item display name

#

###################################################################################

Write-Host ""

$EnumDisplayName = read-host "Enter the Display name of the Enum"

Write-Host ""

Write-Host ""

#Find Matching Enums

$MatchingEnums = Get-SCSMEnumeration | ? {$_.Displayname -eq $EnumDisplayName}

$EnumCount = 0

Write-Host "Results: $($MatchingEnums.Count) Enum Matches for: $EnumDisplayName"

Write-Host "=========================================================="

$EnumPath = ""

#Loop through each Enum that matches the Display Name Entered to get the path

Foreach ($Enum in $MatchingEnums)

{

$EnumPath = $Enum.Displayname

If ($Enum.Parent -ne $null)

{

Do {

#Get the current child's parent info

$Parent = Get-SCSMEnumeration -Id ($Enum.parent.Id)

$EnumPath = $EnumPath + " -> " + $Parent.DisplayName

#Reset the parent to be the next Child in the then loop

$Enum = $Parent

} Until ($Enum.Parent -eq $null)

}# End of childparent not null

$EnumPath

} # End foreach child

Write-Host ""

Write-Host "=========================================================="

Example Output:


Script 3: Dump the current Incident Classifications and Tier Queues

If you are updating classifications or tier queues, it is a good idea to dump the current data first. This provides a backup and a file you can use to roll back to if anything goes wrong. It also provides a file that you can analyse in Excel to work out what data needs to change.

Change the export path to suit your environment: $ExportPath = C:\Temp\IRList.csv

Note: this only gets basic information that is stored directly on the Incident ie no relationships like Assigned To User, Created by User etc.

IREnumDumpCSV.ps1

###################################################################################

#

# Get a list of All Incidents Classification and Tier Queue Enums (no path)

#

###################################################################################

$ExportPath = "C:\Temp\IRList.csv"

$IncidentsArray = @()

$WorkItemClass = Get-SCSMClass System.WorkItem.Incident$

$AllIncidents = Get-SCSMObject -Class $WorkItemClass

ForEach ($Incident in $AllIncidents)

{

$IRExportObject = New-Object System.Object

$IRExportObject | Add-Member -type NoteProperty -name ID -Value $Incident.ID

$IRExportObject | Add-Member -type NoteProperty -name Title -Value $Incident.Title

$IRExportObject | Add-Member -type NoteProperty -name TierQueue -Value $Incident.TierQueue.DisplayName

$IRExportObject | Add-Member -type NoteProperty -name TierQueueEnum -Value $Incident.TierQueue.Name

$IRExportObject | Add-Member -type NoteProperty -name Classification -Value $Incident.Classification.DisplayName

$IRExportObject | Add-Member -type NoteProperty -name ClassificationEnum -Value $Incident.Classification.Name

$IRExportObject | Add-Member -type NoteProperty -name Source -Value $Incident.Source.DisplayName

$IRExportObject | Add-Member -type NoteProperty -name CreatedDate -Value $Incident.CreatedDate

$IncidentsArray += $IRExportObject

} # End of For Each Incident

$IncidentsArray | Sort ID -Descending | Export-csv $ExportPath -NoTypeInformation

Script 4 & 5: Import CSV file to Update Specific Incidents

This script will update a csv file of specific incidents. The script uses specific Enum name values as these are unique. Use the Script 3 above as a starting point to dump the existing values and add a new columns for NewClassificationEnum or NewTierQueueEnum to update the required property.

Example CSV Input file for Updating Classifications


UpdateClassificationFromCSV.ps1

###################################################################################

#

# Import CSV File to Update Incident Classification

#

# Required CSV file Columns ID, NewClassificationEnum

#

###################################################################################

$WorkItemClass = Get-SCSMClass System.WorkItem.Incident$

$UpdatedIncidentsArray = Import-Csv C:\Temp\IRListUpdated.csv

ForEach ($IR in $UpdatedIncidentsArray)

{

$WorkItemObject = Get-SCSMObject -Class $WorkItemClass -Filter "Id -eq $($IR.ID)"

# Remove line below if you DO NOT want to update Classification

$WorkItemObject | Set-SCSMObject -Property Classification -Value $IR.NewClassificationEnum

Write-Host "Updated $($IR.ID)"

} # End for each IR

UpdateTierQueueFromCSV.ps1

###################################################################################

#

# Import CSV File to Update Tier Queues

#

# Required CSV file Columns ID, NewTierQueueEnum

#

###################################################################################

$WorkItemClass = Get-SCSMClass System.WorkItem.Incident$

$UpdatedIncidentsArray = Import-Csv C:\Temp\IRListUpdated.csv

ForEach ($IR in $UpdatedIncidentsArray)

{

$WorkItemObject = Get-SCSMObject -Class $WorkItemClass -Filter "Id -eq $($IR.ID)"

# Remove line below if you DO NOT want to update Support Group (Tier Queue)

$WorkItemObject | Set-SCSMObject -Property TierQueue -Value $IR.NewTierQueueEnum

Write-Host "Updated $($IR.ID)"

} # End for each IR

Script 6 & 7: Update All Classifications that Match a Value

These two scripts will allow you to replace one classification list value with another list value based on their display names or the Enum Name. Be careful as if the scripts matches, it will be updated. The list values must be created before the script runs.

The Display Name script will check to see that both the new and old display names are unique and will produce an error on the screen if they are not. If they are unique it will run and will display on screen the Incidents that were updated.

The Enum Name script assumes that the values are unique and does not check for unique values.

UpdateClassificationDisplayName.ps1

###################################################################################

#

# Get a Specific Enumeration Value and Copy it to ALL Incidents that match a specific Classification

# The script assumes a unique list value for each Display name

#

###################################################################################

##### Change these values to your List Display Names #####

$OriginalEnumDisplayName = "List Value"

$NewEnumDisplayName = "New Value"

# Change this if using a different class from Incident

$rootClassificationEnum = Get-SCSMEnumeration -Name IncidentClassificationEnum$

#Gets all the Enums from the root Incident Classification level

$AllClassificationChildEnum = get-scsmchildenumeration -Enumeration $rootClassificationEnum

# Define the Incident class

$WorkItemClass = Get-SCSMClass System.WorkItem.Incident$

# Get all Incidents

$AllIncidents = Get-SCSMObject -Class $WorkItemClass

#Counters for Enum matches

$NewEnumMatch=0

$OldEnumMatch=0

# This takes both list value Display Names and gets the related Enum object. It sets a counter to check how many Enums match the display name

ForEach ($Enum in $AllClassificationChildEnum)

{

If ($Enum.displayname -eq $NewEnumDisplayName)

{

$NewEnum = $Enum

Write-Host "New Enum: `"$($NewEnum.Displayname)`" with Name: $($NewEnum.name) "

$NewEnumMatch++

} # End of IF

If ($Enum.displayname -eq $OriginalEnumDisplayName)

{

$OldEnum = $Enum

Write-Host "Original Enum: `"$($OldEnum.Displayname)`" with Name: $($OldEnum.name) "

$OldEnumMatch++

} # End of IF

} # End of ForEach Enum

Write-Host ""

Write-Host ""

#Both Enum Display Names have to be Unique for the update to work ie the result should be 2 (`1+1)

$EnumMatch = $NewEnumMatch + $OldEnumMatch

If ($EnumMatch -eq 2)

{

ForEach ($Incident in $AllIncidents)

{

If ($Incident.Classification.DisplayName -eq $OriginalEnumDisplayName)

{

$Incident | Set-SCSMObject -Property Classification -Value $NewEnum.name

Write-Host "Updated $($Incident.ID) to Classification: $NewEnumDisplayName"

} # End of IF

} # End of ForEach Incident

} # End if EnumMatch -eq

Else

{

Write-Host ""

Write-host "This script requires a unique display name for the both list values"

Write-Host " New List Value Matches: $NewEnumMatch"

Write-Host " Old List Value Matches: $OldEnumMatch"

}

UpdateClassificationEnumName.ps1

###################################################################################

#

# Uses a Specific Enumeration Name Value and Copies it to ALL Incidents that match another Enum Name

# As Enum Names are unique, the script does not check for unique values

#

###################################################################################

##### Change these values to your Enums #####

$OriginalEnumName = "Enum.a88bd20ba087494383d7f534fef2fcee"

$NewEnumName = "IncidentClassificationEnum.Email"

# Change this if using a different class from Incident

$rootClassificationEnum = Get-SCSMEnumeration -Name IncidentClassificationEnum$

#Gets all the Enums from the root Incident Classification level

$AllClassificationChildEnum = get-scsmchildenumeration -Enumeration $rootClassificationEnum

# Define the Incident class

$WorkItemClass = Get-SCSMClass System.WorkItem.Incident$

# Get all Incidents

$AllIncidents = Get-SCSMObject -Class $WorkItemClass

# Get the Original and New Enums

ForEach ($Enum in $AllClassificationChildEnum)

{

If ($Enum.Name -eq $OriginalEnumName)

{

$OldEnum = $Enum

Write-Host "Original Enum: $($OldEnum.Name) with Display Name: $($OldEnum.DisplayName) "

} # End of IF

If ($Enum.Name -eq $NewEnumName)

{

$NewEnum = $Enum

Write-Host "New Enum: $($NewEnum.Name) with Display Name: $($NewEnum.DisplayName) "

} # End of IF

} # End of ForEach Enum

Write-Host ""

Write-Host ""

# Loop through all incidents looking for a match on the Original Classification Enum Name and replace with the new Enum

ForEach ($Incident in $AllIncidents)

{

If ($Incident.Classification.Name -eq $OriginalEnumName)

{

$Incident | Set-SCSMObject -Property Classification -Value $NewEnum.name

Write-Host "Updated $($Incident.ID) to Classification: $($NewEnum.DisplayName)"

} # End of IF

} # End of ForEach Incident

#PowerShell #SCSM #SCSM2016

© 2020 Xapity PTY LTD ABN: 81 611 883 482

  • White RSS Icon
  • White YouTube Icon
  • White Vimeo Icon
  • Facebook Clean
  • Twitter Clean