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:  https://blogs.technet.microsoft.com/kevinholman/2009/06/10/creating-custom-dynamic-computer-groups-based-on-registry-keys-on-agents/

I later updated that sample MP here:  https://blogs.technet.microsoft.com/kevinholman/2016/12/04/extending-windows-computer-class-from-registry-keys-in-scom/


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 #================================================================================= Param($SourceId,$ManagedEntityId) # For testing discovery manually in PowerShell: # $SourceId = '{00000000-0000-0000-0000-000000000000}' # $ManagedEntityId = '{00000000-0000-0000-0000-000000000000}' #================================================================================= # Constants section - modify stuff here: $CSVPath = "\\server\share\serverlist.csv" # Assign script name variable for use in event logging $ScriptName = "DemoCSV.Windows.Computer.Extended.Class.Discovery.Script.ps1" #================================================================================= #================================================================================= # function Is-ClassMember # Purpose: To ensure we only return discovery data for computers that # already exist in SCOM, otherwise it will be rejected # Arguments: # -$InstanceDisplayName - The name of the object instance like 'servername.domain.com' #================================================================================== function Is-ClassMember { param($InstanceDisplayName) If ($InstanceDisplayName -in $ComputerNames) { $value = "True" } Else { $value = "False" } Return $value } # End of function Is-ClassMember # Gather script start time $StartTime = Get-Date $MServer = $env:COMPUTERNAME # Gather who the script is running as $WhoAmI = whoami # Load MOMScript API $momapi = New-Object -comObject MOM.ScriptAPI # Load SCOM Discovery module $DiscoveryData = $momapi.CreateDiscoveryData(0, $SourceId, $ManagedEntityId) # Log an event for the script starting $momapi.LogScriptEvent($ScriptName,7777,0, "Script is starting. Running, as $WhoAmI.") # Clear any previous errors if($Error) { $Error.Clear() } # Import the OperationsManager module and connect to the management group Try { $SCOMPowerShellKey = "HKLM:\SOFTWARE\Microsoft\System Center Operations Manager\12\Setup\Powershell\V2" $SCOMModulePath = Join-Path (Get-ItemProperty $SCOMPowerShellKey).InstallDirectory "OperationsManager" Import-module $SCOMModulePath } Catch { $momapi.LogScriptEvent($ScriptName,7778,2, "Unable to load the OperationsManager module, Error is: $error") } Try { New-DefaultManagementGroupConnection $MServer } Catch { $momapi.LogScriptEvent($ScriptName,7778,2, "Unable to connect to the management server: $MServer. Error when calling New-DefaultManagementGroupConnection. Error is: $error") } # Get all instances of a existing Windows Computer class # We need this to check and make sure each computer in the CSV exists in SCOM or discvoery data will be rejected $WindowsComputers = Get-SCOMClass -DisplayName "Windows Computer" | Get-SCOMClassInstance $ComputerNames = $WindowsComputers.DisplayName $ComputerCount = $ComputerNames.count # Log an event for command ending $momapi.LogScriptEvent($ScriptName,7777,0, "Get all Windows Computers has completed. Returned $ComputerCount Windows Computers.") # Clear any previous errors if($Error) { $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,7777,0, "CSV file was found at $CSVPath") } Else { # Log an event for CSV path bad $momapi.LogScriptEvent($ScriptName,7778,2, "CSV file was NOT found at $CSVPath This is a fatal script error, ending script. Error is $Error") exit } # Query the CSV file to get the servers and properties $CSVContents = Import-Csv $CSVPath # 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 Windows Computer in SCOM $IsSCOMComputer = Is-ClassMember $FQDN If ($IsSCOMComputer -eq "True") { $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) } #End If } #End foreach # Return Discovery Items $DiscoveryData # Return Discovery Bag to the command line for testing (does not work from ISE): # $momapi.Return($DiscoveryData) $CSVMatchComputerCount = $i $CSVRowCount = $CSVContents.Count # End script and record total runtime $EndTime = Get-Date $ScriptTime = ($EndTime - $StartTime).TotalSeconds # Log an event for script ending and total execution time. $momapi.LogScriptEvent($ScriptName,7777,0, "Script has completed. CSV returned $CSVRowCount computers. SCOM returned $ComputerCount Computers. Discovery returned $CSVMatchComputerCount matching computers from the CSV and SCOM. Runtime was $ScriptTime seconds")


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
Computer:      SCOMA1.opsmgr.net
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:



Comments (2)

  1. T. Robijns says:

    Hello Kevin,
    Wonderful post, as always.
    Small unrelated suggestion: If your function "Is-ClassMember" would return the boolean $true/$false instead of the string"true"/"false", you can use it immediately in your IF clause:

    #Check and see if the $FQDN value contains a computer that exists as a Windows Computer in SCOM
    $IsSCOMComputer = Is-ClassMember $FQDN
    If ($IsSCOMComputer -eq "True")

    would then become:
    If (Is-ClassMember -InstanceDisplayName $FQDN)

    Thank you for the enormous insight into SCOM

  2. steven says:

    how can we change to what info to be discover by the SCOM you have declared like tier, groupid n owner

Skip to main content