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). 

image

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.

image

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

image

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:  https://gallery.technet.microsoft.com/SCOM-Management-Pack-VSAE-2c506737

<?xml version="1.0" encoding="utf-8"?> <ManagementPack ContentReadable="true" SchemaVersion="2.0" OriginalSchemaVersion="1.1" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <Manifest> <Identity> <ID>Demo.DiscoverClassByServerName</ID> <Version>1.0.0.0</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="http://www.w3.org/2001/XMLSchema" /> <xsd:element name="SyncTime" type="xsd:string" xmlns:xsd="http://www.w3.org/2001/XMLSchema" /> <xsd:element name="TimeoutSeconds" type="xsd:integer" xmlns:xsd="http://www.w3.org/2001/XMLSchema" /> <xsd:element name="DebugLogging" type="xsd:boolean" xmlns:xsd="http://www.w3.org/2001/XMLSchema" /> <xsd:element name="ComputerNameList" type="xsd:string" xmlns:xsd="http://www.w3.org/2001/XMLSchema" /> </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>

4 Comments

  1. CyrAz

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

    $Config/ComputerNameList$

    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.

Leave a Reply

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