Menu Close

Extending Windows Computer class from a CSV file in SCOM

Years ago – I wrote a post on customizing the “Windows Computer” class, showing how to use registry keys to add properties to the “Windows Computer” class, to make creating custom groups much simpler.  You can read about the details of how and why here:

I later updated that sample MP here:


However, I was recently at a customer, and they felt stamping reg keys on all their servers would be too much work.  Additionally, they didn’t have a CMDB, or Authoritative system that recovered all their computers, and their important properties.  In this case, they used a spreadsheet for that.  So, I recommended we use a CSV based on their spreadsheet, to pull back this data into SCOM, using the CSV file as the authoritative record for their servers.


Here is an example of the CSV:



We can write an extended class of Windows Computer, and a script based discovery to read in this CSV, and add each column as a class property in SCOM.

Here is the class definition:

<TypeDefinitions> <EntityTypes> <ClassTypes> <ClassType ID="DemoCSV.Windows.Computer.Extended.Class" Accessibility="Public" Abstract="false" Base="Windows!Microsoft.Windows.Computer" Hosted="false" Singleton="false" Extension="false"> <Property ID="TIER" Type="string" AutoIncrement="false" Key="false" CaseSensitive="false" MaxLength="256" MinLength="0" Required="false" Scale="0" /> <Property ID="GROUPID" Type="string" AutoIncrement="false" Key="false" CaseSensitive="false" MaxLength="256" MinLength="0" Required="false" Scale="0" /> <Property ID="OWNER" Type="string" AutoIncrement="false" Key="false" CaseSensitive="false" MaxLength="256" MinLength="0" Required="false" Scale="0" /> </ClassType> </ClassTypes> </EntityTypes> </TypeDefinitions>


The discovery will target the “All Management Servers Resource Pool” class.  This class is hosted by ONE of the management servers at any given time, and by doing this we will have high availability for the discovery workflow.

The script will read the CSV file, get the FQDN of each row in the CSV, then compare that to a list of all computers in SCOM.  If the computer exists in SCOM, it will add the properties to the discovery.  There is a “constants” section in the script for you to change relevant information:

# Constants section – modify stuff here:
$CSVPath = “\\server\share\serverlist.csv”


Here is the script:

#================================================================================= # Extend Windows Computer class from CSV # # Author: Kevin Holman # v1.2 #================================================================================= Param($SourceId,$ManagedEntityId) # Manual Testing section - put stuff here for manually testing script - typically parameters: #================================================================================= # $SourceId = '{00000000-0000-0000-0000-000000000000}' # $ManagedEntityId = '{00000000-0000-0000-0000-000000000000}' #================================================================================= # Constants section - modify stuff here: #================================================================================= # Assign script name variable for use in event logging. $ScriptName = "DemoCSV.Windows.Computer.Extended.Class.Discovery.ps1" $EventID = "7777" $CSVPath = "\\server\share\serverlist.csv" #================================================================================= # Starting Script section - All scripts get this #================================================================================= # Gather the start time of the script $StartTime = Get-Date #Set variable to be used in logging events $whoami = whoami # Load MOMScript API $momapi = New-Object -comObject MOM.ScriptAPI #Log script event that we are starting task $momapi.LogScriptEvent($ScriptName,$EventID,0,"`n Script is starting. `n Running as ($whoami).") #================================================================================= # Discovery Script section - Discovery scripts get this #================================================================================= # Load SCOM Discovery module $DiscoveryData = $momapi.CreateDiscoveryData(0, $SourceId, $ManagedEntityId) #================================================================================= # Connect to local SCOM Management Group Section - If required #================================================================================= # I have found this to be the most reliable method to load SCOM modules for scripts running on Management Servers # Clear any previous errors $Error.Clear() # Import the OperationsManager module and connect to the management group $SCOMPowerShellKey = "HKLM:\SOFTWARE\Microsoft\System Center Operations Manager\12\Setup\Powershell\V2" $SCOMModulePath = Join-Path (Get-ItemProperty $SCOMPowerShellKey).InstallDirectory "OperationsManager" Import-module $SCOMModulePath New-DefaultManagementGroupConnection "localhost" IF ($Error) { $momapi.LogScriptEvent($ScriptName,$EventID,1,"`n FATAL ERROR: Unable to load OperationsManager module or unable to connect to Management Server. `n Terminating script. `n Error is: ($Error).") EXIT } #================================================================================= # Begin MAIN script section #================================================================================= # Get all instances of a existing Health Service class - this can take a long time. # We need this list of SCOM agents, so we can only submit discovery data for service in SCOM otherwise the discovery will reject the data, and this will clean up deleted Windows Computer objects that will remain until the next discovery runs $HS = Get-SCOMClass -Name "Microsoft.SystemCenter.Healthservice" | Get-SCOMClassInstance $HSNames = $HS.DisplayName $HSCount = $HSNames.count #Log event $momapi.LogScriptEvent($ScriptName,$EventID,0,"`n Get all Health Service Objects has completed. `n Returned ($HSCount) Health Service objects.") # Clear any previous errors $Error.Clear() #Test the CSV path and make sure we can read it: IF (Test-Path $CSVPath) { # Log an event for CSV path good $momapi.LogScriptEvent($ScriptName,$EventID,0,"`n CSV file was found at ($CSVPath)") } ELSE { # Log an event for CSV path bad $momapi.LogScriptEvent($ScriptName,$EventID,2,"`n FATAL ERROR: CSV file was NOT found at ($CSVPath). `n Terminating script.") EXIT } # Query the CSV file to get the servers and properties $CSVContents = Import-Csv $CSVPath $CSVRowCount = $CSVContents.Count $momapi.LogScriptEvent($ScriptName,$EventID,0,"`n Found ($CSVRowCount) rows in the CSV file. `n Beginning loop.") # Loop through the CSV and add discovery data for existing SCOM computers $i=0; FOREACH ($Row in $CSVContents) { # Get the FQDN and assign it to a variable $FQDN = $Row.FQDN #Check and see if the $FQDN value contains a computer that exists as a HealthService in SCOM IF ($FQDN -in $HSNames) { $i=$i+1 # Get each property in your CSV and assign it to a variable $TIER = $row.TIER $GROUPID = $row.GROUPID $OWNER = $row.OWNER # Create discovery data for each computer that exists in both the CSV and SCOM $Inst = $DiscoveryData.CreateClassInstance("$MPElement[Name='DemoCSV.Windows.Computer.Extended.Class']$") $Inst.AddProperty("$MPElement[Name='Windows!Microsoft.Windows.Computer']/PrincipalName$", $FQDN) $Inst.AddProperty("$MPElement[Name='DemoCSV.Windows.Computer.Extended.Class']/TIER$", $TIER) $Inst.AddProperty("$MPElement[Name='DemoCSV.Windows.Computer.Extended.Class']/GROUPID$", $GROUPID) $Inst.AddProperty("$MPElement[Name='DemoCSV.Windows.Computer.Extended.Class']/OWNER$", $OWNER) $DiscoveryData.AddInstance($Inst) } } $CSVMatchComputerCount = $i $momapi.LogScriptEvent($ScriptName,$EventID,0,"`n CSV returned ($CSVRowCount) computers. `n SCOM returned ($HSCount) Computers. `n Discovery returned ($CSVMatchComputerCount) matching computers from the CSV and SCOM.") #================================================================================= # End MAIN script section # Discovery Script section - Discovery scripts get this #================================================================================= # Return Discovery Items Normally $DiscoveryData # Return Discovery Bag to the command line for testing (does not work from ISE) # $momapi.Return($DiscoveryData) #================================================================================= # End of script section #================================================================================= #Log an event for script ending and total execution time. $EndTime = Get-Date $ScriptTime = ($EndTime - $StartTime).TotalSeconds $momapi.LogScriptEvent($ScriptName,$EventID,0,"`n Script Completed. `n Script Runtime: ($ScriptTime) seconds.") #================================================================================= # End of script


You will need to change the path of the script file, and make sure your management server action account has read permissions to the share and file.


You can review the discovery data in discovered inventory:




I also added rich logging to the script to understand what is happening:


Log Name:      Operations Manager
Source:        Health Service Script
Date:          12/4/2016 2:53:18 PM
Event ID:      7777
Level:         Information
DemoCSV.Windows.Computer.Extended.Class.Discovery.Script.ps1 : Script has completed.  CSV returned 5 computers.  SCOM returned 26 Computers.  Discovery returned 5 matching computers from the CSV and SCOM.  Runtime was 5.8906066 seconds


I am attaching the sample MP file, along with the sample CSV registry file, at the following location:


WARNING:  Whenever you “extend” a class that is normally hosted by the agent (Like Windows Computer) but you are running the discovery on a management server such as in this case, there is an unintended consequence.  If you delete the agent, you will notice the Extended class instances and Windows Computer objects are not deleted.  This is because they are still “discovered” but your extending discovery.  This discovery needs to run again before the objects will be “undiscovered” and marked as deleted.  These objects will be orphans, and hosted by the management servers until they are deleted.  Therefore, you might need to run the discoveries more often, such as every hour, or every 4 hours, to ensure they get deleted in a timely fashion and don’t impact your environment.


  1. Jarrad


    Great post, really helped me a lot!
    In my version I changed the get-scomClass to a SQL query though. It can be a lot faster 🙂

    • Kevin Holman

      Yep – going through the SDK can slow things down. SQL is much faster. However, for MOST customers, this should not be a big deal…. most customers can get the SCOM class instances for the HealthService within 60 seconds. Did yours take longer than that?

  2. Kumar

    Much thanks and Great Post Kevin it did help me, one thing i wanted to let you know this doesnot pull workgroup computers would you be of help here 🙂

    • Kevin Holman

      Workgroup computers should be fine – you just need to ensure the name in the CSV matches the name in SCOM.

  3. Adam Hulek

    Hi Kevin,

    I have a question regarding the class definition and the property “Extension”. What would be a difference in your example if you have used Extension=”true”?

  4. Feras

    Hi Kevin!
    We have deleted many agents at the same time, but the extended computer discovery is still active on those deleted servers and we can see them on Windows Computer.

    We have checked our database and found them under discovery, do you think it is ok to delete them from Ops Manager database, or is there any other way? We tried to run the script quite often but we didn’t get any help.

    • Kevin Holman

      Is the extended class discovery still running?

      When you delete an agent from the console – the extended class will remain, UNTIL the extended class discovery runs again and “UNDISCOVERS” the orphans.

      How long ago did you delete the agents, and has the extended class discovery run since then?

  5. Pingback:Coffee Break: The SCOM Clinic: Your Questions Answered (Part 2) - SCOMathon

  6. John

    Hi Kevin,

    That’s for the code. It is awesome. Now that I have these extend properties, how to I use them? I tried creating a group but I cannot find the Extend class in the Dymanic Members section of making anew group. Any Ideas?



  7. Pingback:SCOM FAQs and expert answers - SquaredUp

Leave a Reply

Your email address will not be published.