Menu Close

How to discover instances of a SCOM class based on Computer Name

When creating rules and monitors in SCOM, we need a good, specific class for targeting.

Often times, a customer will come to you and say “I just wanted to monitor for three services on those 5 computers”

In SCOM – doing something that sounds so easy, actually isn’t.  What customers would often LIKE to do, is simply create a group of Windows Computers, and just target the service monitors to the group.  We all know – that does not work.  You cannot target workflows at groups, as the workflow will not execute on the group members. 

You “could” create the service monitors disabled, target something generic like “Windows Server Operating System” then override the monitors to enabled for the group.  That works, but it is really ugly.

SCOM uses a discovery model for targeting, and sometimes there just isn’t an easy way to discover what you want.

I have a fragment that might help with that. 

This fragment is called Class.And.Discovery.Script.ByServerName.mpx.  This fragment will let you discover instances of a custom class, and will only discover an instance if the computer name matches a static list.  Here is how to use it:

I will import this fragment using Silect MP Author Pro, but it works the same in Visual Studio (VSAE). 


I only need to provide the standard stuff, then a comma separated list of server names that I want to be members of the class.  That’s IT!

Then, I import the MP, and need to override the discovery.  The discovery in this MP is disabled by default (because I don’t want to run this script on ALL machines…. so I will override the discovery just for the servers that I want it to run on.


When I go look in discovered inventory – I can see my instances have discovered:


I can now write simple rules and monitors, and have a good class for targeting.  If you want to remove a server, simply edit the discovery and take the name out.  Add new ones, and just make sure to enable the discovery so it runs for them.

My MP example is below, or you can get the fragment from my fragment library:

<?xml version="1.0" encoding="utf-8"?> <ManagementPack ContentReadable="true" SchemaVersion="2.0" OriginalSchemaVersion="1.1" xmlns:xsd="" xmlns:xsl=""> <Manifest> <Identity> <ID>Demo.DiscoverClassByServerName</ID> <Version></Version> </Identity> <Name>Demo.DiscoverClassByServerName</Name> <References> <Reference Alias="SC"> <ID>Microsoft.SystemCenter.Library</ID> <Version>7.0.8433.0</Version> <PublicKeyToken>31bf3856ad364e35</PublicKeyToken> </Reference> <Reference Alias="Windows"> <ID>Microsoft.Windows.Library</ID> <Version>7.5.8501.0</Version> <PublicKeyToken>31bf3856ad364e35</PublicKeyToken> </Reference> <Reference Alias="Health"> <ID>System.Health.Library</ID> <Version>7.0.8433.0</Version> <PublicKeyToken>31bf3856ad364e35</PublicKeyToken> </Reference> <Reference Alias="System"> <ID>System.Library</ID> <Version>7.5.8501.0</Version> <PublicKeyToken>31bf3856ad364e35</PublicKeyToken> </Reference> </References> </Manifest> <TypeDefinitions> <EntityTypes> <ClassTypes> <ClassType ID="Demo.MyCustomerApp.DiscoverByName.Class" Accessibility="Public" Abstract="false" Base="Windows!Microsoft.Windows.LocalApplication" Hosted="true" Singleton="false" Extension="false" /> </ClassTypes> </EntityTypes> <ModuleTypes> <DataSourceModuleType ID="Demo.MyCustomerApp.DiscoverByName.Class.Discovery.DS" Accessibility="Internal" Batching="false"> <Configuration> <xsd:element name="IntervalSeconds" type="xsd:integer" xmlns:xsd="" /> <xsd:element name="SyncTime" type="xsd:string" xmlns:xsd="" /> <xsd:element name="TimeoutSeconds" type="xsd:integer" xmlns:xsd="" /> <xsd:element name="DebugLogging" type="xsd:boolean" xmlns:xsd="" /> <xsd:element name="ComputerNameList" type="xsd:string" xmlns:xsd="" /> </Configuration> <OverrideableParameters> <OverrideableParameter ID="IntervalSeconds" Selector="$Config/IntervalSeconds$" ParameterType="int" /> <OverrideableParameter ID="SyncTime" Selector="$Config/SyncTime$" ParameterType="string" /> <OverrideableParameter ID="TimeoutSeconds" Selector="$Config/TimeoutSeconds$" ParameterType="int" /> <OverrideableParameter ID="DebugLogging" Selector="$Config/DebugLogging$" ParameterType="bool" /> <OverrideableParameter ID="ComputerNameList" Selector="$Config/ComputerNameList$" ParameterType="string" /> </OverrideableParameters> <ModuleImplementation Isolation="Any"> <Composite> <MemberModules> <DataSource ID="DS" TypeID="Windows!Microsoft.Windows.TimedPowerShell.DiscoveryProvider"> <IntervalSeconds>$Config/IntervalSeconds$</IntervalSeconds> <SyncTime>$Config/SyncTime$</SyncTime> <ScriptName>Demo.MyCustomerApp.DiscoverByName.Class.Discovery.DS.ps1</ScriptName> <ScriptBody> #================================================================================= # SCOM Script to discover instances of a class when provided a list of ComputerNames # # Author: Kevin Holman # v1.0 #================================================================================= param($SourceId,$ManagedEntityId,[string]$ComputerName,[string]$MGName,$DebugLogging,[string]$ComputerNameList) # Manual Testing section - put stuff here for manually testing script - typically parameters: #================================================================================= # $SourceId = '{00000000-0000-0000-0000-000000000000}' # $ManagedEntityId = '{00000000-0000-0000-0000-000000000000}' # [string]$ComputerName = "SQL1.OPSMGR.NET" # [string]$MGName = "MGNAME" # $DebugLogging = "false" # [string]$ComputerNameList = "SQL1,SQL2A" #================================================================================= # Constants section - modify stuff here: #================================================================================= # Assign script name variable for use in event logging. # ScriptName should be the same as the ID of the module that the script is contained in $ScriptName = "Demo.MyCustomerApp.DiscoverByName.Class.Discovery.DS.ps1" $EventID = "1240" #================================================================================= # 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 #================================================================================= # Discovery Script section - Discovery scripts get this #================================================================================= # Load SCOM Discovery module $DiscoveryData = $momapi.CreateDiscoveryData(0, $SourceId, $ManagedEntityId) #================================================================================= # Begin MAIN script section #================================================================================= #Build NetBIOSName from ComputerName $NetBIOSName = $ComputerName.Split(".")[0] $NetBIOSName = $NetBIOSName.Trim() #Log script event that we are starting task $momapi.LogScriptEvent($ScriptName,$EventID,0,"`nScript is starting. `nRunning as ($whoami). `nManagement Group: ($MGName). `nComputerName: ($ComputerName). `nNetBIOSName: ($NetBIOSName). `nDebugLogging: ($DebugLogging). `nComputerNameList: ($ComputerNameList)") #Show concept of additional debug logging IF ($DebugLogging.ToUpper() -eq "TRUE") { $momapi.LogScriptEvent($ScriptName,$EventID,0,"`n This event is being logged because debug Logging was set to: ($DebugLogging)") } #Build an array of the ComputerNameList $ComputerNameList = $ComputerNameList.Replace(" ","") [array]$ComputerNameListArray = $ComputerNameList.Split(",") #Check to see if this NetBIOS Name for the computer is in the ComputerNameList provided by the discovery IF ($ComputerNameListArray -contains $NetBIOSName) { #This is a match. We should discover an instance of the class $instance = $DiscoveryData.CreateClassInstance("$MPElement[Name='Demo.MyCustomerApp.DiscoverByName.Class']$") $instance.AddProperty("$MPElement[Name='Windows!Microsoft.Windows.Computer']/PrincipalName$", $ComputerName) $instance.AddProperty("$MPElement[Name='System!System.Entity']/DisplayName$", $ComputerName) $DiscoveryData.AddInstance($instance) # Log an event that objects were discovered $momapi.LogScriptEvent($ScriptName,$EventID,0,"`n Discovery script is returning discoverydata objects for ($ComputerName).") } ELSE { #Do nothing. This computer is not in the ComputerNameList } # Return Discovery Items Normally $DiscoveryData # Return Discovery Bag to the command line for testing (does not work from ISE) # $momapi.Return($DiscoveryData) #================================================================================= # End MAIN script section # 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 </ScriptBody> <Parameters> <Parameter> <Name>SourceId</Name> <Value>$MPElement$</Value> </Parameter> <Parameter> <Name>ManagedEntityId</Name> <Value>$Target/Id$</Value> </Parameter> <Parameter> <Name>ComputerName</Name> <Value>$Target/Host/Property[Type="Windows!Microsoft.Windows.Computer"]/PrincipalName$</Value> </Parameter> <Parameter> <Name>MGName</Name> <Value>$Target/ManagementGroup/Name$</Value> </Parameter> <Parameter> <Name>DebugLogging</Name> <Value>$Config/DebugLogging$</Value> </Parameter> <Parameter> <Name>ComputerNameList</Name> <Value>$Config/ComputerNameList$</Value> </Parameter> </Parameters> <TimeoutSeconds>$Config/TimeoutSeconds$</TimeoutSeconds> </DataSource> </MemberModules> <Composition> <Node ID="DS" /> </Composition> </Composite> </ModuleImplementation> <OutputType>System!System.Discovery.Data</OutputType> </DataSourceModuleType> </ModuleTypes> </TypeDefinitions> <Monitoring> <Discoveries> <Discovery ID="Demo.MyCustomerApp.DiscoverByName.Class.Discovery" Enabled="false" Target="Windows!Microsoft.Windows.Server.OperatingSystem" ConfirmDelivery="false" Remotable="true" Priority="Normal"> <Category>Discovery</Category> <DiscoveryTypes> <DiscoveryClass TypeID="Demo.MyCustomerApp.DiscoverByName.Class" /> </DiscoveryTypes> <DataSource ID="DS" TypeID="Demo.MyCustomerApp.DiscoverByName.Class.Discovery.DS"> <IntervalSeconds>86333</IntervalSeconds> <SyncTime /> <TimeoutSeconds>120</TimeoutSeconds> <DebugLogging>false</DebugLogging> <ComputerNameList>SQL2014A,SQL2014B</ComputerNameList> </DataSource> </Discovery> </Discoveries> </Monitoring> <LanguagePacks> <LanguagePack ID="ENU" IsDefault="true"> <DisplayStrings> <DisplayString ElementID="Demo.DiscoverClassByServerName"> <Name>Demo DiscoverClassByServerName</Name> </DisplayString> <DisplayString ElementID="Demo.MyCustomerApp.DiscoverByName.Class"> <Name>Demo MyCustomerApp DiscoverByName Class</Name> </DisplayString> <DisplayString ElementID="Demo.MyCustomerApp.DiscoverByName.Class.Discovery"> <Name>Demo MyCustomerApp DiscoverByName Class Discovery</Name> </DisplayString> </DisplayStrings> </LanguagePack> </LanguagePacks> </ManagementPack>


  1. CyrAz

    We could even avoid running Powerhsell with a simple filtered class snapshot datamapper, wouldn’t we?
    Something like


    ContainsSubstring $Target/Host/Property[Type=”Windows!Microsoft.Windows.Computer”]/PrincipalName$

      • Kevin Holman

        I thought about that, even reg or WMI providers…. the problem is you’d have to do some kind of complex regex. You cannot do a contains substring because you could have server1 and server11, etc. I could not figure out a perfect regex that would not discover servers that you didn’t want, and handle the commas, whether the user put spaces or not, FQDN or not, etc…. so I stuck with PS. I would bet if someone is REALLY good with Regex, they could figure it out and make this less “heavy handed” with a script.

  2. tiboro

    I’m missing some point here: Trying to avoid to create a disabled monitor with an override for a group (which is ugly), you end up creating a disabled discovery with an override for a group…

    • Kevin Holman

      I can see how that might be confusing. I’ll try to explain:

      1. You don’t have to enable it for specific machines, but you can if you want. Enabling it for all is no big deal.
      2. Enabling a disabled discovery is MUCH cleaner than mass targeting monitors to generic classes.
      3. The whole point is to build a good CLASS for targeting, for notifications, for grouping, for views. Without a class, life is much harder for application level monitoring.

  3. Matt

    This is excellent and exactly what I was looking for thanks!
    I do have a question though, How would I add a subclass to this? I am looking to discover an application by Servername and discover it’s individual components by server name too but have not had much luck. Essentially I’d like to be able to have a class and dependant subclasses all discoverable by servername so I can then use them as targets for windows service monitors to generate availability reports etc.

  4. Todd Henard

    Many thanks for this! This is exactly what I was looking for. I have a slew of app service monitors targeted to Windows Server… you said – it works – but its ugly.

    I do have a qq and sure hoping you will chime in.

    I have built a new 2019 env, and am hoping to get this working before I start cutting over from 2012.
    I have completed all the steps. I have only 2 test systems in this new envioronment. I have included both systems in my comma seperated .txt file.
    I checked the UNC path in the .xml, and file open as desired. I can see both systems receive the MP with the discovery. However they do not populate in the Discovered Inventory.

    Any pointers?

  5. Todd Henard

    I see the Health Service Script completes in the agents event log:

    Script Completed. Script Runtime: (0.0624787) seconds.

    I replaced actual app name/org id with your example of “fab.MyApp”.

  6. Todd Henard

    I see the the required references:

    This fragment depends on references:
    RequiredReference: Alias=”System”, ID=”System.Library”
    RequiredReference: Alias=”Windows”, ID=”Microsoft.Windows.Library”

    My reference versions:
    ID: Microsoft.Windows.Library 7.5.8501.0
    ID: System.Library 7.5.8501.0

  7. Todd Henard

    I got it working!

    And now i see this:# [string]$ComputerNameList = “SQL1,SQL2A”

    I had a file and unc path, thats why it was not working.

    Thanks Kevin!

Leave a Reply

Your email address will not be published. Required fields are marked *