Programmatically Creating Groups

Well, it looks from my last post that I took about a eight month hiatus from the blog.  Fortunately, I've been doing a bunch of OpsMgr in that time, so I have a bunch of content to get out here.  Just a matter of taking the time to write it up.

The one I'll go with first is programmatically creating groups.  Now creating a single group is pretty easy in the Operations Console, so creating a single one from a command line isn't going to get anyone excited.  What is exciting though is if you have a bunch of groups to create - especially if several of these are subgroups.

I'll leave you to figure out the complexity of providing a list of groups - maybe reading from a text file or something.  That's just straight PowerShell work.  What I will give you though is the code to create a new group.  You should be able to use this base code to do whatever complex and repetitive group creation that you might need.

First of all, there is no cmdlet to create a group.  You're going to have to manually create a Microsoft.EnterpriseManagement.Monitoring.CustomMonitoringObjectGroup object, set its properties, and then save it to the management group using the InsertCustomMonitoringObjectGroup method on the management group.  That part's easy.  It will be when I give you the code in a minute anyway.  The tough part of the exercise is setting the membership formula, and we need to go through that before we get into the code.

Membership Formula

The membership formula is an XML expression defining the logic for what objects should be members of the group.  The easiest way to figure out how to build this formula is to configure a group using the dialog box in the Operations Console and then copy out the resulting XML.  There are a couple of things you need to know about this though,  so let me provide a bit of explanation and a couple of examples.

When you export the management pack, you'll find the membership rule in the discovery for the group.  It might take you a little bit of searching through the different discoveries if you aren't familiar with the XML schema of a management pack, but it shouldn't take too long.  A typical discovery will look something like the XML below.

 <Discovery ID="UINameSpace9e378de9416945c4820c4cacfd0475fa.Group.DiscoveryRule" Enabled="true" Target="UINameSpace9e378de9416945c4820c4cacfd0475fa.Group" ConfirmDelivery="false" Remotable="true" Priority="Normal"> 
  <Category>Discovery</Category> 
  <DiscoveryTypes> 
    <DiscoveryRelationship TypeID="SCIG!Microsoft.SystemCenter.InstanceGroupContainsEntities" /> 
  </DiscoveryTypes> 
  <DataSource ID="GroupPopulationDataSource" TypeID="SystemCenter!Microsoft.SystemCenter.GroupPopulator"> 
    <RuleId>$MPElement$</RuleId> 
    <GroupInstanceId>$MPElement[Name="UINameSpace9e378de9416945c4820c4cacfd0475fa.Group"]$</GroupInstanceId> 
    <MembershipRules> 
      <MembershipRule> 
        <MonitoringClass>$MPElement[Name="SCIG!Microsoft.SystemCenter.InstanceGroup"]$</MonitoringClass> 
        <RelationshipClass>$MPElement[Name="SCIG!Microsoft.SystemCenter.InstanceGroupContainsEntities"]$</RelationshipClass> 
        <IncludeList> 
          <MonitoringObjectId>d47ea9a0-a039-d399-bec3-4b305269b57e</MonitoringObjectId> 
        </IncludeList> 
      </MembershipRule> 
    </MembershipRules> 
  </DataSource> 
</Discovery>

The only part you want is the MembershipRules section, and that’s what I’ll describe below.

Membership Formula Examples

First, let's look at a typical dynamic membership.  The following dialog box shows a criteria that you might create.

image

That logic would translate to the following membership rule which could be used as is.  There are no GUIDs here to worry about.

 <MembershipRule> 
  <MonitoringClass>$MPElement[Name="Windows!Microsoft.Windows.Computer"]$</MonitoringClass> 
  <RelationshipClass>$MPElement[Name="SCIG!Microsoft.SystemCenter.InstanceGroupContainsEntities"]$</RelationshipClass> 
  <Expression> 
    <And> 
      <Expression> 
        <RegExExpression> 
          <ValueExpression> 
            <Property>$MPElement[Name="Windows!Microsoft.Windows.Computer"]/PrincipalName$</Property> 
          </ValueExpression> 
          <Operator>MatchesWildcard</Operator> 
          <Pattern>lgb*</Pattern> 
        </RegExExpression> 
      </Expression> 
      <Expression> 
        <RegExExpression> 
          <ValueExpression> 
            <Property>$MPElement[Name="Windows!Microsoft.Windows.Computer"]/IPAddress$</Property> 
          </ValueExpression> 
          <Operator>MatchesWildcard</Operator> 
          <Pattern>10.1.*</Pattern> 
        </RegExExpression> 
      </Expression> 
    </And> 
  </Expression> 
</MembershipRule>

 

How about a group with explicit members?  Now this is probably a bad example, because you probably won't pragmatically be creating groups with explicit members, but it is possible.

 

image

This generates the following membership rule which includes GUIDs.  These GUIDs really can't be swapped out though since they refer to specific objects.  If you were to programmatically  create something like this, you would presumably use Get-MonitoringObject in Command Shell to get the GUID of the object you're interested in and then add it to the XML.

 <MembershipRule> 
  <MonitoringClass>$MPElement[Name="SystemCenter!Microsoft.SystemCenter.ManagedComputer"]$</MonitoringClass> 
  <RelationshipClass>$MPElement[Name="SCIG!Microsoft.SystemCenter.InstanceGroupContainsEntities"]$</RelationshipClass> 
  <IncludeList> 
    <MonitoringObjectId>b3e9befe-173b-78a2-3e41-e19ecb58d9d5</MonitoringObjectId> 
    <MonitoringObjectId>0852e1eb-f854-eb88-2f7d-a1bb0794d18f</MonitoringObjectId> 
    <MonitoringObjectId>6e813ffe-2267-a191-ccbb-566d4ddf95bc</MonitoringObjectId> 
    <MonitoringObjectId>98e3ee12-54c0-f14c-506c-4113bb8f1dcb</MonitoringObjectId> 
  </IncludeList> 
</MembershipRule>

 

Subgroups are something you very well might want to use.  One use of programmatically creating groups is to create a complex group structure which may contain several levels of different groups.

image

 

This would generate the following XML, and in this case we would want to swap out that GUID which refers to the subgroup (Sample Group - Level 2 in the example).

 <MembershipRule> 
  <MonitoringClass>$MPElement[Name="SCIG!Microsoft.SystemCenter.InstanceGroup"]$</MonitoringClass> 
  <RelationshipClass>$MPElement[Name="SCIG!Microsoft.SystemCenter.InstanceGroupContainsEntities"]$</RelationshipClass> 
  <IncludeList> 
    <MonitoringObjectId>e499513e-4e67-142e-f1f6-53b24b5195bb</MonitoringObjectId> 
  </IncludeList> 
</MembershipRule>

This would work better with the following.  The end result is the same since we'll still end up with the GUID of the subgroup.  but this example could be installed in other management groups and would make a lot more sense when working with code.

 <MembershipRule> 
  <MonitoringClass>$MPElement[Name="SCIG!Microsoft.SystemCenter.InstanceGroup"]$</MonitoringClass> 
  <RelationshipClass>$MPElement[Name="SCIG!Microsoft.SystemCenter.InstanceGroupContainsEntities"]$</RelationshipClass> 
  <IncludeList> 
    <MonitoringObjectId>$MPElement[Name='bwren.GroupDemo.SampleGroupLevel2']$</MonitoringObjectId> 
  </IncludeList> 
</MembershipRule>

 

Finally, we need to talk about empty groups since this is very common situation when programmatically creating groups.  You may automate the creation of a large group structure relying on users to add objects to these groups through the Operations Console once the management pack is loaded.  Empty groups have a very specific structure which is below.

     <MembershipRule Comment="EMPTY_RULE_8eadaced-59c8-4ebc-a4e4-b8428a374442"> 
      <MonitoringClass>$MPElement[Name="SC!Microsoft.SystemCenter.AllComputersGroup"]$</MonitoringClass> 
      <RelationshipClass>$MPElement[Name="SCIG!Microsoft.SystemCenter.InstanceGroupContainsEntities"]$</RelationshipClass> 
      <Expression> 
        <SimpleExpression> 
          <ValueExpression> 
            <Value>True</Value> 
          </ValueExpression> 
          <Operator>Equal</Operator> 
          <ValueExpression> 
            <Value>False</Value> 
          </ValueExpression> 
        </SimpleExpression> 
      </Expression> 
    </MembershipRule> 

As you can see, the basic logic is to check if True equals False.  Since this will obviously never be a true statement, we have no objects that match the criteria.  The other thing you need though is the comment with that specific string on the MembershipRule tab.  There’s no real value in going into the background on that.  Just know that you need it.  If you don’t include it, the group will get created, but you’ll get a pretty ugly error when you try to modify the membership of that group from the Operations Console.

Management Pack Aliases

If you aren’t familiar with management pack aliases, they are separated from management object names with a ! in all the examples above.  An alias is required if you are referring to an object in another management pack.  The alias to that management pack must be in the References section of the management pack containing your group.  In the examples above, you can see the alias SCIG which refers to the System Center Instances Group management pack.  As such, the Reference section would need to contain the following entry.

 <Reference Alias="SCIG"> 
  <ID>Microsoft.SystemCenter.InstanceGroup.Library</ID> 
  <Version>6.0.6278.0</Version> 
  <PublicKeyToken>31bf3856ad364e35</PublicKeyToken> 
</Reference>

Your management pack might use a different alias for Microsoft.SystemCenter.InstanceGroup.Library.  I just created a new management pack with the Operations Console, and it used MicrosoftSystemCenterInstanceGroupLibrary6164070. Obviously, you will need to use whatever alias your management pack is using.  I have some code that will figure out what alias is being used for a particular management pack, but that will have to wait for a later post.

Creating the Group

Now that you know how to create the formula, you need the code to create the group.  We’re using the InsertCustomMonitoringObjectGroup method of the ManagementPack class.  We need to pass that a CustomMonitoringObjectGroup which is created with a group name, a group display name, and a formula.  The group name is created from a combination of a namespace and a name.  The namespace acts as the standard prefix for all objects in the management pack.  It will append that the the group name that we specify to build a complete name.  In the example below, I’m using a namespace of bwren.Groups and a group name of Gargantua.  The full name of the resulting group will be bwren.Groups.Gargantua.

With all that explanation, I’ll let the following code speak for itself.  This example creates an empty group, but all you would need to do to create a different membership rule is to replace the $formula variable with one of the examples above.

 $mpName = 'bwren.Groups' 
$namespace = 'bwren.groups' 
$groupName = 'gargantua' 
$groupDisplayName = 'Gargantua'


$formula =  '<MembershipRule Comment="EMPTY_RULE_8eadaced-59c8-4ebc-a4e4-b8428a374442">' + ` 
            '<MonitoringClass>$MPElement[Name="SCIG!Microsoft.SystemCenter.InstanceGroup"]$</MonitoringClass>' + ` 
            '<RelationshipClass>$MPElement[Name="SCIG!Microsoft.SystemCenter.InstanceGroupContainsEntities"]$</RelationshipClass>' + ` 
            '<Expression>' + ` 
            '<SimpleExpression>' + ` 
            '<ValueExpression>' + ` 
            '<Value>True</Value>' + ` 
            '</ValueExpression>' + ` 
            '<Operator>Equal</Operator>' + ` 
            '<ValueExpression>' + ` 
            '<Value>False</Value>' + ` 
            '</ValueExpression>' + ` 
            '</SimpleExpression>' + ` 
            '</Expression>' + ` 
            '</MembershipRule>' 

$group = New-Object Microsoft.EnterpriseManagement.Monitoring.CustomMonitoringObjectGroup($namespace,$groupName,$groupDisplayName,$formula) 
$mp = Get-ManagementPack -name $mpName 
$mp.InsertCustomMonitoringObjectGroup($group)