.\Matthew Long

{An unsorted collection of thoughts}

Scripting Relationship Discovery in Operations Manager 2007 / 2012

Posted by Matthew on March 3, 2012

One of the things I’ve found myself doing for several recent customer management packs is the discovery of relationships between objects belonging to 3rd party management packs and in-house components.  In simpler terms, often linking an application to the SQL databases it relies on automatically.  This is most useful because now you don’t have to create Distributed Applications by hand for every application to build up a dependency health model, and you don’t have to replicate all the knowledge contained inside the SQL management packs.

The technique itself is actually fairly straightforward, but when trying to gather the necessary knowledge I found it all over the place and often amongst busy, confusing articles.  So here is the concise knowledge you need to implement a relationship discovery of a 3rd party object inside a VBScript discovery.

*Update* Ok so in hindsight it wasn’t that concise.  It is, at least, comprehensive.


The first thing you need to be aware of before trying to create relationships are some of the constraints you have to work within with Operations Manager.

Rule 1 : Hosting location

If either your source or target object is hosted on an operations manager agent, then you are constrained as to the type of relationships you can create.  You can only create a relationship to perform health rollup between objects only if:

  1. Both objects are hosted on the same agent (i.e two server roles)
  2. One object is hosted on an agent, and the other is not hosted (or the object is hosted by a non hosted object).  Essentially the second object must exist on the RMS /Management servers.
  3. Neither object is hosted (both on the RMS/MS)

You can check if an object is hosted by looking at its class definition and seeing if it has the “Hosted” attribute set to “true”.

In one case, where we had a hosted application on one server and SQL and IIS components hosted on another, what this meant was having to create a non-hosted version of the application (discovered with the local hosted copy), which then had relationships to its local application object and components.  As far as the users were concerned, the non-hosted object was the instance in SCOM of their application.

The diagram below illustrates a scenario where an application has a dependency on a (already discovered and managed) SQL database.  Here we want to create a Containment relationship and rollup health monitoring of the database to the local application.  The diagram shows the two valid scenarios where you can create a relationship and the invalid (and unfortunately, most common one).

Rule 2: Source and target existence in SCOM

In order for Operations Manager to accept the relationship inside your discovery data that is returned by the discovery script, both the source and target of the relationship must either already exist, or be defined in the discovery data.  This is often fine for the source, since we have just discovered our object so we know that exists.

The issue can often be with the target, because if we attempt to define a relationship to an object assuming it already exists in SCOM and it doesn’t (say because an agent is not installed on that host, or the discovery for the object is disabled) then all discovery data from that discovery instance is discarded by SCOM with no alert in the console!  I can’t explain why this occurs, this is just my experience.  If anyone reading this knows why this occurs, feel free to get in touch!

So if your discovery were to discover your application, and 5 component relationships and only 1 of those objects doesn’t actually exist, nothing will be discovered!

What this means practically is you have two options, both of which have downsides..

  1. Discover both the source and target in your discovery, to ensure that both objects exist.
  2. Discover the source object, then have a second discovery targeting your object that creates the relationship.

The first option means that you know your objects exist, and you don’t have to actually rediscover every property since if you only discover the Key properties of the object SCOM knows it’s the same object as the pre-existing and works its magic in the background using Reference Counting to merge them.  Reference counting means that if two objects have identical keys (and in the case of hosted objects, identical parent object keys) SCOM assumes they are the same object, and simply increments the reference count for the object.  As long as the reference count remains above zero then the object continues to exist, with all discovered properties.

The first downside to this approach is that if the two objects are hosted on different agents, you will need to enable Proxy discovery on the agent running your discovery.  This is because hosted applications ultimately specify the Computer they are hosted upon, and you are now creating objects for another computer (even though it’s just a reference count).  The second downside is that when the 3rd party object (such as a SQL DB) is undiscovered by its original discovery, the object will live on in SCOM as long as you are still discovering it because the reference count is still 1, even if that object truly no longer exists (which could confuse the hell out of the SQL admins when they see a database still exists on an old server post DB migration).  It will also be missing any properties that the original discovery populated which you didn’t, which may cause all sorts of alerts to start appearing in the SCOM console when values expected by monitors are unpopulated.

So, the second option (only discover your source object, and have a second discovery to create the relationship) has the advantage that if your 3rd party object doesn’t exist, you won’t have a “ghost” object left in SCOM, but your application will exist because it was already discovered as part of a different workflow.  You also now aren’t responsible for reverse engineering how your target object (and any properties) were discovered.  The downside here is that any other objects found in your second discovery still will not be present.  So if you had an app that had SQL relationships found in one discovery and IIS websites found in another, and one IIS website was missing, all the IIS websites would not be present, but your app and SQL DBs would be.

There isn’t really a right answer, you’ve got to answer this one individually with your organisation and/or customer and determine which behaviour is better.  My gut preference is when writing MPs for customers is to use the second option, and to have a property on my object that states how many component objects there are supposed to be.  That way we don’t generate false alerts or weird behaviour, and application owners can still see when an object is missing.  When writing an MP for yourself, you can ensure that all agents hosting components get agents and that everything is discovered properly.

Rule 3:  Identifying your objects

As we will see in a moment (we’ll get to some code soon, promise!) in order to create a relationship you need to be able to uniquely identify the source and target objects.  Your source object is probably your own object you’ve just discovered (or the target of your relationship discovery if you are using two discoveries, either way you know which object you want).

Unfortunately unless you get fancy with the SCOM SDK and Powershell discoveries, the only way to specify another object outside of your discovery is to recreate it in your discovery script with the same key properties.  In order to use the SDK, you’ve got to ensure that your machine running the discovery has the SCOM SDK libs availible, which is a pretty rare occurance outside of management servers.

You don’t have to actually submit the object in your discovery results (depending on what you are doing with Rule 2, of course), but you need to be able to specify all key properties of the target object, and if it is hosted, it’s parent object’s key properties.  If the hosting parent doesn’t have a key, then specify its parent’s keys.  Eventually every hosted object has a parent with keys because you’ll hit a Windows.Computer or Unix.Computer object.

So for a working example: SQL Databases.  In order to uniquely identify a SQL DB you’ll need to know:

  1. It’s key property, the Database Name.
  2. It’s hosting object’s key property, the SQL Instance name.
  3. SQL Databases are hosted, and therefore the principal name of the agent computer these objects are hosted on (either the local machine’s principal name, or the remote machines principal name if the DB is on a remote SQL server).

Sounds simple, but what about objects that don’t have a key?  Well since there is no key, there must only ever be one instance on that host so that’s fine, SCOM knows which object you are referring to.  The troublesome objects are ones like IIS websites.  They have a key property, but it’s an obscure one that isn’t commonly known by other components (in this case the Site ID, a value only unique on the IIS server and meaningless from an external connection sense).  In these scenario’s if your datasource doesn’t know what the key is, you are either going to have to try and remotely dial in and discover it yourself, or resort to some nasty workarounds using Group Populator modules which are really a different article in themselves.

Down to business – the code

Here in my example code, I’m going to assume I have already performed my steps that discover my application property values and I have a series of variables containing my application properties and my target object’s keys.  A common scenario is I have determined which SQL database they use from a connection string written in a config file or the local registry.  So now I want to discover my hosted local application, and a relationship to its SQL database (which may or may not be hosted locally, I’ll still have to submit the PrincipalName property either way since SQL DBs are hosted objects).

'Already have appName, appProperty1 and appProperty2 appComputerName defined and populated
'Likewise, databaseName, dbInstanceName and databaseServer are already populated variables
Dim scomAPI, discoveryData, applicationInstance, databaseInstance, relationshipInstance
Set scomAPI = CreateObject("MOM.ScriptAPI")
Set discoveryData = scomAPI.CreateDiscoveryData(0, "$MPElement$", "$Target/Id$")
'Discover my Application object
Set applicationInstance = discoveryData.CreateClassInstance("$MPElement[Name='MySampleMP.ApplicationClass']$")
    Call applicationInstance.AddProperty("$MPElement[Name='MySampleMP.ApplicationClass']/Name$", appName)
    Call applicationInstance.AddProperty("$MPElement[Name='MySampleMP.ApplicationClass']/FirstProperty$", appProperty1)
    Call applicationInstance.AddProperty("$MPElement[Name='MySampleMP.ApplicationClass']/SecondProperty$", appProperty2)
    Call applicationInstance.AddProperty("$MPElement[Name='Windows!Microsoft.Windows.Computer']/PrincipalName$", appComputerName)
'If my discovery is targeting my prexisting object (rule 2, second option), then i don't need to call the below line.
Call discoveryData.AddInstance(applicationInstance)

'Now create my target relationship object, which i can optionally submit with my discovery, depending on what i am doing about Rule 2
Set databaseInstance = discoveryData.CreateClassInstance("$MPElement[Name='MSSQL!Microsoft.SQLServer.Database']$")
    Call databaseInstance.AddProperty("$MPElement[Name='MSSQL!Microsoft.SQLServer.Database']/DatabaseName$", databaseName)
    Call databaseInstance.AddProperty("$MPElement[Name='MSSQL!Microsoft.SQLServer.ServerRole']/InstanceName$", dbInstanceName)
    Call databaseInstance.AddProperty("$MPElement[Name='Windows!Microsoft.Windows.Computer']/PrincipalName$", databaseServer)
'If i am going to discover this object as well as part of my Rule 2 decision, then call the below line.  Otherwise ignore it.
'Call discoveryData.AddInstance(databaseInstance)

'Now it's time to create my relationship!
Set relationshipInstance = discoveryData.CreateRelationshipInstance("$MPElement[Name='MySampleMP.Relationship.AppContainsDatabase']$")
     relationshipInstance.Source = applicationInstance
     relationshipInstance.Target = databaseInstance
Call discoveryData.AddInstance(relationshipInstance)
'Return all discovered objects.
Call scomAPI.Return(discoveryData)

That’s it! If I wanted to create multiple different relationships I’d just have to create multiple relationship instances and an instance of each target object (most likely using a For Each loop).  Again we don’t need to submit the target object instance (or even the source, if it already exists) in our discovery data unless we want to, we only need it so that we can specify it in the relationship instance.

Of course, if you are actually creating a relationship between two objects defined in your management pack, then you’ll probably want to submit both the source and target objects in your discovery (assuming they don’t already exist from a different discovery).

If you do have a scenario where you need to create a relationship based on an object with an unknown key value but other known properties, leave a comment below and I’ll look at writing another blog post detailing some of the ones I’ve come up with.

Hope this helps someone else out in the future, and saves them the experimentation and research time I lost to this rather simple activity!




10 Responses to “Scripting Relationship Discovery in Operations Manager 2007 / 2012”

  1. […] Long recently had a good blog post entitled “Scripting Relationship Discovery in Operations Manager 2007 / 2012” and we’ll use that as a basis for our own management pack and relationship […]

  2. tommy said

    It seems to work on 2007 R2 though – containment from MS-hosted to agent-hosted objects.

    • Matthew said

      Hi Tommy,

      That will work because whilst the object is hosted, it’s not hosted on an Agent but on the Management Server. Probably something I should make more clear in the article, as many management packs take advantage of this.

      Essentially, as long as your containing object is either in the same health service instance (same agent or MS) as the contained object, or the containing object is on the Management Server (hosted or un-hosted) you can create a relationship.

      Kind regards,


      • tommy said

        Let me clarify the scenario:

        Class C is hosted on MS.
        Class S is hosted on the agent.
        Containment relationships are created between C instance and S instances.

        It works for me just fine on SCOM 2007 R2: containment relationships are created and showing up on the console.

        However, on SCOM 2012:
        1) It is leading to the agent (which hosts the S instance) graying out
        2) The C objects (host)are going to an

        Is this limitation introduced in SCOM 2012?

      • Matthew said

        Hi Tommy

        It definitely works in 2012, otherwise distributed Applications and groups wouldn’t function correctly. I’ve seen this behaviour in Scom 2007, in the past caused by failures in native modules crashing the Scom agent.

        When the Agent goes into a grey state, are you getting any relevant errors in the SCOM agent/ms event log?

        What data source are you using for your discovery, may I ask? Going out on a limb, if it’s a Powershell based module and you have yet to apply CU1 for system center 2012 then I’d look to do that as I know there are circumstances where the RTM agent can crash when running PS based modules.

        If you can post your discovery config/script, that may help with determining the issue.

        Kind regards,


  3. Alfonso Diaz said

    What about when you need to discovery some attributes of one class from another Agent …. Example, let said you have a MP that discovery some attributes from an specific server ?

    • Matthew said

      Hi Alfonso,

      This is a supported scenario, and you can make use of two features to do this – Reference Counting and Proxy Discovery. These two features are discussed in the above article under Rule 2, though in a slightly different context. For reference, the related paragraph is..

      “Reference counting means that if two objects have identical keys (and in the case of hosted objects, identical parent object keys) SCOM assumes they are the same object, and simply increments the reference count for the object. As long as the reference count remains above zero then the object continues to exist, with all discovered properties.

      The … downside to this approach is that … , you will need to enable Proxy discovery on the agent running your discovery. ”

      So in order to discover class attributes from one agent for another, you’ll need to enable Agent Proxy for the 2nd agent, and then discover an instance of your class with the same Key properties AND hosted key properties (typically the computer Principal name, in the case of windows hosted roles and applications) on your 2nd agent, along with and additional attributes you wish to update. This means that your specific server will need to know the computer name(s) of the servers that originally discovered the instance of the class (along with any other key properties). If the class is unhosted (say a distributed component such as a cluster) then you’ll only need the key properties of the class itself.

      You don’t need to provide values for any other attributes, if they are not specified the Reference counting process will merge attributes from multiple discoveries together. Any attributes you do specify will overwrite the original discoveries’ values however.

      Sorry for the late response, hope that helps!

  4. Curtiss said

    how about doing this in powershell, and how about using a powershell script to populate a group?

    get-scomclass -displayname “*ole db check group” |
    where managementpackname -eq ‘managementPackX’|

    gives me a list of OLE DB database watcher groups (created in the scom console with templates) objects I want to add to GroupY. how could I get this information into a group populator discovery for GroupY?

    • Matthew said

      Hi Curtiss

      Powershell discovery scripts are the same as VBScript discoveries, with two differences. Firstly, you’re going to call New-Object -ComType “MOM.ScriptAPI” to create the scom API object. The second is that rather than calling the .Return() method on your API object, just output the discoverydata object to powershell (as if you were displaying it in the console – using write-host doesn’t work).

      In terms of a group populator module – you won’t be able to feed this information directly into a GP module as it’s a Datasource (always first in the workflow) and it is looking for class property or containment relationships in order to find objects. That said, all the group populator is doing is creating containment relationships between the group instance and any class instances it finds. As such, this gives you two options:

      -Use a group populator module with logic to find all objects contained by your existing database watcher group instances (this operator can cause performance problems on the SCOM DB if you don’t configure it correctly or overuse it, so be careful here).

      -Create a Powershell discovery targeted at Group Y that collects all the objects you need, and then creates a Microsoft.SystemCenter.InstanceGroupContainsEntities relationship between the group (source) and the object (Target). As this discovery is targeted at the Group, it will automatically run on your RMS/All Management Servers resource pool, so you know it can connect to localhost in order to access the SCOM management group via powershell.

      Hope that helps!

  5. Graham said

    Great article as always. For information, I did find something “interesting” while using this as a basis for writing a Timed PowerShell Discovery. Using $databaseinstance = throws an error when used within PowerShell as it appears that $data with anything after it, even as part of a parameter name, will throw an error when you try to build in Visual Studio. One of those quirks …

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s