More on Group Membership and Calculations

A coworker of mine, Joel Pothering, put together a great article on groups in SCOM. I am posting it here on his behalf:

I wanted to post a message that gives more details on how to author group membership rules. I’ll start with a review of Jakub Oleksy’s blog entry, found here:

https://blogs.msdn.com/jakuboleksy/archive/2006/11/15/creating-and-updating-groups.aspx

Jakub’s example shows how you can use the SDK to create a group with a corresponding membership rule, and then update it. I’ll concentrate on the different types of membership rules (or formulas) that you can create beyond what this example shows. You should still use Jakub’s example as a guide to create your own – in short, we’ll just modify the formula variable in his example.

Simple group membership rule

Here’s the first membership rule in Jakub’s example:

<MembershipRule>

  <MonitoringClass>$MPElement[Name="Windows!Microsoft.Windows.Computer"]$</MonitoringClass>

  <RelationshipClass>$MPElement[Name="InstanceGroup!Microsoft.SystemCenter.InstanceGroupContainsEntities"]$</RelationshipClass>

</MembershipRule>

This is the simplest form of a group membership rule. It groups every instance (MonitoringObject) of the MonitoringClass type specified in the MonitoringClass element. What this really means is that the system will create a new instance of a relationship (MonitoringRelationshipObject) of the type specified in the RelationshipClass element for every instance of MonitoringClass. The source of the relationship is the group and the target is the MonitoringClass instance.

Group membership rules are therefore discovery rules that “discover” relationships between a group and the instances you want it to contain. So you’re not discovering entities in your enterprise, but relationships between entities you already discovered. The target of this discovery rule is the group type itself. The data source module for the rule is Microsoft.SystemCenter.GroupPopulator, defined in the SystemCenter management pack. Let’s refer to this entire group membership rule system as simply GroupCalc.

One other note about this simple membership rule: use this if you want to group another group, that is, create a subgroup. Simply specify the subgroup’s type in the MonitoringClass element, and its singleton instance will become a member.

Using an expression to filter members

You probably want to be more specific about what instances you want your group to contain beyond just their type. You can filter instances using an expression in the membership rule.  What we’ll do is add an Expression element after RelationshipClass to filter on computers with an IsVirtualMachine property value of ‘false’. The membership rule now becomes:

<MembershipRule>

  <MonitoringClass>$MPElement[Name="Windows!Microsoft.Windows.Computer"]$</MonitoringClass>

  <RelationshipClass>$MPElement[Name="InstanceGroup!Microsoft.SystemCenter.InstanceGroupContainsEntities"]$</RelationshipClass>

  <Expression>

    <SimpleExpression>

      <ValueExpression>

        <Property>$MPElement[Name="Windows!Microsoft.Windows.Computer"]/IsVirtualMachine$</Property>

      </ValueExpression>

      <Operator>Equal</Operator>

      <ValueExpression>

        <Value>False</Value>

      </ValueExpression>

    </SimpleExpression>

  </Expression>

</MembershipRule>

The SimpleExpression element defines a left and right operand, and an operator, and GroupCalc evaluates this for every instance of computer. Here’s a simplified representation of this predicate:

Computer.IsVirtualMachine = ‘False’

The left operand is a Property element and therefore references a property of MonitoringClass. The right operand is a Value element, which can be a string or a number. In this case it is a string that represents a boolean value. GroupCalc knows that since IsVirtualMachine is a boolean property, ‘False’ should be converted to a boolean value of 0.

You can apply logical operators on each predicate defined by a SimpleExpression. For example, let’s change the membership rule to filter based on this simplified representation:

Computer.IsVirtualMachine = ‘False’ AND ( Computer.NetbiosDomainName LIKE ‘EASTCOAST’ OR Computer.NetbiosDomainName LIKE ‘WESTCOAST’ )

The expression in the membership rule becomes:

  <Expression>

    <And>

      <Expression>

        <SimpleExpression>

          <ValueExpression>

            <Property>$MPElement[Name="Windows!Microsoft.Windows.Computer"]/IsVirtualMachine$</Property>

          </ValueExpression>

          <Operator>Equal</Operator>

          <ValueExpression>

            <Value>False</Value>

          </ValueExpression>

        </SimpleExpression>

      </Expression>

      <Expression>

        <Or>

          <Expression>

            <SimpleExpression>

              <ValueExpression>

                <Property>$MPElement[Name="Windows!Microsoft.Windows.Computer"]/NetbiosDomainName$</Property>

              </ValueExpression>

              <Operator>Like</Operator>

              <ValueExpression>

                <Value>EASTCOAST</Value>

              </ValueExpression>

            </SimpleExpression>

          </Expression>

          <Expression>

            <SimpleExpression>

              <ValueExpression>

                <Property>$MPElement[Name="Windows!Microsoft.Windows.Computer"]/NetbiosDomainName$</Property>

              </ValueExpression>

              <Operator>Like</Operator>

              <ValueExpression>

                <Value>WESTCOAST</Value>

              </ValueExpression>

            </SimpleExpression>

          </Expression>

        </Or>

      </Expression>

    </And>

  </Expression>

The left side of And has an Or expression, and the parenthesis we required above is implied. The two SimpleExpressions in Or use the Like operator -- similar to T-SQL’s LIKE -- to do a case insensitive comparison.

To see what other operators we can use with SimpleExpression, let’s examine parts of GroupCalc’s configuration schema, Microsoft.SystemCenter.GroupPopulationSchema, located in the SystemCenter management pack. Here is the definition of SimpleExpression:

  <xsd:complexType name="SimpleCriteriaType">

    <xsd:sequence maxOccurs="1" minOccurs="0">

      <xsd:annotation>

        <xsd:documentation>Expression comparing two values.</xsd:documentation>

      </xsd:annotation>

      <xsd:element name="ValueExpression" type="ValueExpressionType"/>

      <xsd:element name="Operator" type="CriteriaCompareType"/>

      <xsd:element name="ValueExpression" type="ValueExpressionType"/>

    </xsd:sequence>

  </xsd:complexType>

Here is the definition of CriteriaCompareType:

  <xsd:simpleType name="CriteriaCompareType">

      <xsd:restriction base="xsd:string">

          <xsd:enumeration value="Like">

              <xsd:annotation>

                  <xsd:documentation>LIKE</xsd:documentation>

              </xsd:annotation>

          </xsd:enumeration>

          <xsd:enumeration value="NotLike">

              <xsd:annotation>

                  <xsd:documentation>NOT LIKE</xsd:documentation>

              </xsd:annotation>

          </xsd:enumeration>

          <xsd:enumeration value="Equal">

              <xsd:annotation>

                  <xsd:documentation>Equal to.</xsd:documentation>

              </xsd:annotation>

          </xsd:enumeration>

          <xsd:enumeration value="NotEqual">

              <xsd:annotation>

                  <xsd:documentation>Not equal to.</xsd:documentation>

              </xsd:annotation>

          </xsd:enumeration>

          <xsd:enumeration value="Greater">

              <xsd:annotation>

                  <xsd:documentation>Greater than.</xsd:documentation>

              </xsd:annotation>

          </xsd:enumeration>

          <xsd:enumeration value="Less">

              <xsd:annotation>

                  <xsd:documentation>Less than.</xsd:documentation>

              </xsd:annotation>

          </xsd:enumeration>

          <xsd:enumeration value="GreaterEqual">

              <xsd:annotation>

                  <xsd:documentation>Greator than or equal to.</xsd:documentation>

              </xsd:annotation>

          </xsd:enumeration>

          <xsd:enumeration value="LessEqual">

              <xsd:annotation>

                  <xsd:documentation>Less than or equal to.</xsd:documentation>

              </xsd:annotation>

          </xsd:enumeration>

      </xsd:restriction>

  </xsd:simpleType>

These operators should cover everything you need to do in a SimpleExpression.

Now take a look at ExpressionType in GroupPopulationSchema to see what other expressions we can use:

<xsd:complexType name="ExpressionType">

  <xsd:choice>

    <xsd:element name="SimpleExpression" type="SimpleCriteriaType"/>

    <xsd:element name="UnaryExpression" type="UnaryCriteriaType"/>

    <xsd:element name="RegExExpression" type="RegExCriteriaType"/>

    <xsd:element name="Contains" type="ContainsCriteriaType"/>

    <xsd:element name="NotContains" type="ContainsCriteriaType"/>

    <xsd:element name="Contained" type="ContainedCriteriaType"/>

    <xsd:element name="NotContained" type="ContainedCriteriaType"/>

    <xsd:element name="And" type="AndType"/>

    <xsd:element name="Or" type="OrType"/>

  </xsd:choice>

</xsd:complexType>

We already used SimpleExpression, And, and Or. The UnaryExpression is used for testing null-ness. For example, to test whether a value for Computer.NetbiosDomainName was never discovered (i.e. it is NULL), you can use this expression:

  <Expression>

    <UnaryExpression>

      <ValueExpression>

        <Property>$MPElement[Name="Windows!Microsoft.Windows.Computer"]/NetbiosDomainName$</Property>

      </ValueExpression>

      <Operator>IsNull</Operator>

    </UnaryExpression>

  </Expression>

RegExExpression allows you to use regular expression syntax to test property values. This is similar to what you use in other types of rules in Operations Manager that require regular expressions. Here’s one example that tests the value of NetbiosDomainName for the pattern ‘^WEST’:

  <Expression>

      <RegExExpression>

          <ValueExpression>

              <Property>$MPElement[Name="Windows!Microsoft.Windows.Computer"]/NetbiosDomainName$</Property>

          </ValueExpression>

          <Operator>MatchesRegularExpression</Operator>

          <Pattern>^WEST</Pattern>

      </RegExExpression>

  </Expression>

Containment expressions

Contains allows you to group based on what instances MonitoringClass contains, and Contained on what instances contain MonitoringClass. These are powerful expressions that look beyond attributes of MonitoringClass and allow you to query to see how MonitoringClass is related to other instances.

Let’s look at the SystemCenter management pack again and find an example of a group membership rule that uses Contains. The following is the membership rule for the internal group we use that contains all agent managed computers:

  <MembershipRule>

      <MonitoringClass>$MPElement[Name="SCLibrary!Microsoft.SystemCenter.ManagedComputer"]$</MonitoringClass>

      <RelationshipClass>$MPElement[Name="SCLibrary!Microsoft.SystemCenter.ComputerGroupContainsComputer"]$</RelationshipClass>

      <Expression>

          <Contains>

              <MonitoringClass>$MPElement[Name="SCLibrary!Microsoft.SystemCenter.Agent"]$</MonitoringClass>

          </Contains>

      </Expression>

  </MembershipRule>

An agent managed computer is represented in the system as a ManagedComputer instance that hosts an Agent instance. Hosting is a special type of containment that requires the host instance exists before the hosted instance can exist.

Contains also takes an Expression element. Here’s an example from the SystemCenter management pack that groups all management servers that are not the root management server:

  <Expression>

      <Contains>

          <MonitoringClass>$MPElement[Name="SCLibrary!Microsoft.SystemCenter.CollectionManagementServer"]$</MonitoringClass>

          <Expression>

              <SimpleExpression>

                  <ValueExpression>

                      <Property>$MPElement[Name="SCLibrary!Microsoft.SystemCenter.HealthService"]/IsRHS$</Property>

                  </ValueExpression>

                  <Operator>Equal</Operator>

                  <ValueExpression>

                      <Value>False</Value>

                  </ValueExpression>

              </SimpleExpression>

          </Expression>

      </Contains>

  </Expression>

A management server is a ManagedComputer that contains (hosts) a CollectionManagementServer, which derives from HealthService. A SimpleExpression is used to test the HealthService property IsRHS.  Notice that we now reference properties on the contained MonitoringClass, not the MonitoringClass that we are grouping.

Any of the previously mentioned expression types can be used here, including any type of containment expression.

Both Contains and Contained have compliments, NotContains and NotContained. For example, agentless managed computers (or remotely managed) are computers that are monitored, but do not host a health service (they aren’t agents). Let’s say we want to create a membership rule to group these. One way you can do this is group all instances of ManagedComputer that are not a member -- NotContained by -- the agent managed computer group. Here’s what the membership rule would look like:

<MembershipRule>

  <MonitoringClass>$MPElement[Name="SCLibrary!Microsoft.SystemCenter.ManagedComputer"]$</MonitoringClass>

  <RelationshipClass>$MPElement[Name="InstanceGroup!Microsoft.SystemCenter.InstanceGroupContainsEntities"]$</RelationshipClass>

  <Expression>

    <NotContained>

      <MonitoringClass>SCLibrary!Microsoft.SystemCenter.AgentManagedComputerGroup</MonitoringClass>

    </NotContained>

  </Expression>

</MembershipRule>

Multiple membership rules

You can define multiple membership rules for a single group. Each MembershipRule element is actually independent of the other. It is used primarily to create heterogeneous groups.

Relationship source/target types and inheritance

It’s worth reviewing how inheritance works with GroupCalc, and how you choose the RelationshipClass for your membership rule. Let’s take another look at the membership rule from the SystemCenter management pack for grouping management servers that are not the root management server:

<MembershipRule>

    <MonitoringClass>$MPElement[Name="SCLibrary!Microsoft.SystemCenter.ManagedComputer"]$</MonitoringClass>

    <RelationshipClass>$MPElement[Name="SCLibrary!Microsoft.SystemCenter.ComputerGroupContainsComputer"]$</RelationshipClass>

    <Expression>

        <Contains maxDepth="1">

            <MonitoringClass>$MPElement[Name="SCLibrary!Microsoft.SystemCenter.CollectionManagementServer"]$</MonitoringClass>

            <Expression>

                <SimpleExpression>

                    <ValueExpression>

                        <Property>$MPElement[Name="SCLibrary!Microsoft.SystemCenter.HealthService"]/IsRHS$</Property>

                    </ValueExpression>

                    <Operator>Equal</Operator>

                    <ValueExpression>

                        <Value>False</Value>

                    </ValueExpression>

                </SimpleExpression>

            </Expression>

        </Contains>

    </Expression>

</MembershipRule>

The group (not shown above) is the singleton type Microsoft.SystemCenter.CollectionManagementServerComputersGroup, which derives from Microsoft.SystemCenter.ComputerGroup, an abstract type. The type we want to group is Microsoft.SystemCenter.ManagedComputer, which derives from Microsoft.Windows.Computer. Now, we specified the relationship type Microsoft.SystemCenter.ComputerGroupContainsComputer in the RelationshipClass element, which means GroupCalc creates instances of that type of relationship to group members. What we need to be sure of is that this is a valid relationship type to use. Here’s the definition of ComputerGroupContainsComputer:

<RelationshipType ID="Microsoft.SystemCenter.ComputerGroupContainsComputer Abstract="false" Base="System!System.Containment">

    <Source>Microsoft.SystemCenter.ComputerGroup</Source>

    <Target>System!System.Computer</Target>

</RelationshipType>

Look at the Source and Target types. What these tell us is that the group has to be of a type derived from the abstract type Microsoft.SystemCenter.ComputerGroup, and the instance we’re grouping has to derive from System.Computer, another abstract type. It looks like our group matches -- CollectionManagementServerComputersGroup derives from ComputerGroup. And since Windows.Computer derives from System.Computer, and ManagedComputer derives from Windows.Computer, our grouped instances can be part of this relationship too – all is good.

What happens if we pick the wrong the relationship type? GroupCalc will reject the configuration for the rule, and you’ll see an error in the event log from the runtime. An example of an incorrect relationship type would be the one used in the first example we discussed, Microsoft.SystemCenter.InstanceGroupContainsEntities. Here’s its definition:

<RelationshipType ID="Microsoft.SystemCenter.InstanceGroupContainsEntities" Base="System!System.Containment" Abstract="false"

  <Source>Microsoft.SystemCenter.InstanceGroup</Source>

  <Target>System!System.Entity</Target>

</RelationshipType>

The Source is Microsoft.SystemCenter.InstanceGroup which is a singleton group type that derives directly from the abstract type System.Group – this doesn’t look good. The Target type is the base type of everything, so we’re definitely okay there. So it’s the Source type that makes InstanceGroupContainsEntities invalid with the management server membership rule, because CollectionManagementServerComputersGroup doesn’t derive from InstanceGroup.

The reason you have to be careful is that our verification process for importing management packs will not catch this. We don’t have that level of knowledge, or semantic checks, available in our verification process to help us here. Only GroupCalc has this check in its verification process.

Referenced properties

I have one more note on the membership rule for Microsoft.SystemCenter.CollectionManagementServerComputersGroup. Notice that we are grouping CollectionManagementServer instances, as specified in the MonitoringClass element. In the expression, we reference a property on HealthService, which is a base type of CollectionManagementServer. We do this because you need to specify the type that defines the property. So you cannot do this:

<ValueExpression>

     <Property>$MPElement[Name="SCLibrary!Microsoft.SystemCenter.CollectionManagementServer"]/IsRHS$</Property> // WRONG!!!

</ValueExpression>

This will produce an error from our management pack verification process since there is no such property, which is good – we’ll know right away, before we can even import, that this is incorrect.

What we won’t know right away, and what you need to be careful of, are property references that can pass management pack verification but still can be rejected by GroupCalc. For example, if we were to put in a Windows.Computer property there, it would pass management pack verification – the property is valid after all. But our GroupCalc schema implies that the property referenced must be from the type you’re grouping, or from any of its base types. Again, only GroupCalc has this knowledge, and you’ll get a runtime error logged after GroupCalc rejects the configuration.

Conclusion

GroupCalc can help you in most of your scenarios that involve grouping instances. I hope you get a chance to investigate GroupCalc more thoroughly. If you do and have questions, please don’t hesitate to post these questions to the newsgroup – we’ll be sure to help.

Thanks - Joel