Retrieving SCOM Groups from Management Packs via PowerShell
By Gabriel Taylor
Published September 3, 2014
scom-groups-powershell
Estimated Reading Time: 9 minutes

I received a request from a client the other day to assess their SCOM environment and collect a report displaying the member count for their custom groups. This seemed like a pretty simple request at first glance, especially since I could easily distinguish their custom groups from any others since theirs were all saved in unsealed management packs. When I dove into PowerShell to grab the data, though, I found a few roadblocks between me and the information I wanted. I decided to write up an overview on interacting with SCOM groups via PowerShell to document the process and the roadblocks one might encounter along the way.

 

First things first, let’s start with a basic explanation on how groups are defined in Operations Manager. The most common way of interacting with SCOM groups is via the “Groups” view in the Operations Console’s Authoring workspace, seen here:

 

03 - Groups view in SCOM Console

 

When a group is created via the “Create a New Group” task, a new class is added to the management pack specified in the wizard. Any options entered along the way, including explicit members, dynamic membership rules, excluded members, subgroups, etc., are all stored in the management pack as part of that class’ definition. For example, here is the management pack XML definition for the Active Directory 2008 Domain Controllers group:

<ClassType ID="Microsoft.Windows.Server.2008.AD.DomainControllerComputerGroup" Accessibility="Public" Abstract="false" Base="SCIGL!Microsoft.SystemCenter.InstanceGroup" Hosted="false" Singleton="true" Extension="false" />

 

One thing that is important to note is that the class definition is not the group itself as seen in the Operations Console, rather it is merely the rules used for defining the group. The group objects that you see in the console are instances of their respective classes, in the same way that each Windows Computer object shown in the console is an instance of the Microsoft.Windows.Computer class. Unlike the computer class, though, Group class definitions are “Singleton” classes, meaning that there can be only a single instance per class definition.

 

Something else that can be seen from the example above is that this class definition is based on the class “Microsoft.SystemCenter.InstanceGroup”. As can be learned from the Technet Page on SCOM Groups, there are two main types of groups into which SCOM groups can fall – Computer Groups (Microsoft.SystemCenter.ComputerGroup) and Instance Groups (Microsoft.SystemCenter.InstanceGroup). Groups created in the console are always Instance Groups, while Computer Groups can be created through other means. For the purposes of this blog post, we don’t need to go into the differences between the two; knowledge that they exist is enough.

 

In order to work with these groups via PowerShell, we can use the Get-SCOMGroup cmdlet in the Operations Manager Shell.

 

02 - Get SCOM Groups (with Type)

 

As you can see above, I’ve used Get-SCOMGroup to store all of my SCOM environment’s groups into a variable and displayed the count to see how many groups exist. I’ve also displayed the object type so you can see that each object returned from the Get-SCOMGroup cmdlet is an object of type MonitoringObjectGroup (full name = “Microsoft.EnterpriseManagement.Monitoring.MonitoringObjectGroup”). These MonitoringObjectGroup objects are the class instances created from the group class definitions in the management packs. Going back to the impetus for this post, we need a way to identify in which management pack the class definition for a given group is stored in order to work only with the custom groups in our unsealed management packs. If you use the Get-Member cmdlet on a single group in our collection, you’ll see that the MonitoringObjectGroup class contains a large collection of methods as well as a list of properties, but nowhere does it say what management pack contains our group. There is a property for ManagementPackClassIds, though, and as you can see in here, we can use that to get the management pack containing the group:

 

04 - Getting MP from Group

 

Seeing as what we’re really wanting to do is get the groups from a list of management packs, it seems like we’re going to end up doing a lot of re-work if we go down this path, though. First we’d need to get the groups, then identify their classes, then identify the management packs, then check each to see if they are sealed or unsealed (or whatever our criteria are), before then being able to filter our list finally get the subset of SCOM groups we care about. Since in our example, we already know which management packs we care about, it sure would be a lot faster to start from there and work backwards to the group objects.

 

Fortunately for us, we can do just that. Let’s walk through this process the other direction.

 

First, we want to get our management packs. (In this case, we want the unsealed ones, but this can work using any criteria – so long as we can get the management packs we care about in a variable to begin with, everything afterwards would be the same.) We can retrieve management packs in PowerShell via the Get-SCOMManagementPack cmdlet:

$MPs = Get-SCOMManagementPack | ?{!$_.Sealed}

 

05 - Getting Unsealed MPs

 

The above command gets all unsealed management packs and stores them in a variable. If you use Get-Member on one of the management packs, you can see that the management pack objects contain a large number of methods useful for getting management pack contents. Unfortunately, there is no “GetGroups” method (wouldn’t that be convenient), but as we discussed earlier, the management pack is going to contain the class definitions for our groups, so the “GetClasses” method will suffice.

 

Here’s where things get a little interesting. We know in which management packs we wish to find groups, but we don’t necessarily know which groups are in what management pack, or what their internal names are going to be. We could look all that up via the information above and store it in a variable to use as a filter, but that would defeat the purpose of starting with the management packs (this way is supposed to be faster, remember?). As we discussed above, though, any custom groups are going to be of the Instance Group or Computer Group types. Therefore, we can leverage that information to filter the results and grab just the group classes we are looking for. To do so, we need to know the Id properties of the Instance Group and Computer Group classes, which we can get via another bit of PowerShell:

 

06 - Class Ids

 

We can then use the following PowerShell to cycle through each management pack in our variable, collecting all group classes and storing them in another variable. Note the usage of the GUIDs of our base classes in the Where-Object statement to only extract our group classes. After that, we use the Get-SCOMGroup command again with the Id properties of the classes to get the actual class instances for our group class definitions, leaving us with a variable containing all the group objects derived from specific management packs:

$MPGroupClasses = @()
foreach ($MP in $MPs) {
    $MPGroupClasses += $MP.GetClasses() | ?{$_.Base.Id -eq "4ce499f1-0298-83fe-7740-7a0fbc8e2449" -or $_.Base.Id -eq "0c363342-717b-5471-3aa5-9de3df073f2a"}
}

$MPGroupClasses.Count
$GroupsByMP = Get-SCOMGroup -Id $MPGroupClasses.Id
$GroupsByMP.Count

 

08 - MPGroupClasses and GroupsByMP Creation

 

As you can see in the image above, in my environment, I have 6 group classes stored in unsealed management packs. Filtering those group classes’ Ids through Get-SCOMGroup resulted in 6 MonitoringObjectGroup objects, just as we had expected. From there, it wouldn’t be too hard to use the “GetRelatedMonitoringObjects” method to get lists and counts of contained objects and spit them out in a CSV.

 

Before you go rushing off, remember how I said at the beginning that there were some roadblocks I ran into along the way? Let’s expand our scope and see what happens when we grab every group object in management packs via the method above. Here’s the commands used in the below screenshot:

$GroupsByCmdlet = Get-SCOMGroup
$GroupsByCmdlet.Count

$MPs = Get-SCOMManagementPack
$MPGroupClasses = @()
foreach ($MP in $MPs) {
    $MPGroupClasses += $MP.GetClasses() | ?{$_.Base.Id -eq "4ce499f1-0298-83fe-7740-7a0fbc8e2449" -or $_.Base.Id -eq "0c363342-717b-5471-3aa5-9de3df073f2a"}
}

$MPGroupClasses.Count
$GroupsByMP = Get-SCOMGroup -Id $MPGroupClasses.Id
$GroupsByMP.Count

 

09 - Problems in Paradise

 

Look at the results of the three Count calls. Do you notice how they are all different from each other? Somehow, Get-SCOMGroup returned considerably more objects than our filter on the management pack GetClasses method. Additionally, when the Ids retrieved via GetClasses were passed back to Get-SCOMGroup, something must have gotten lost, because that count is lower still. What gives, I say?

 

It took a little bit of time leveraging Compare-Object, the GetBaseTypes method on the ManagementPackClass object type, and some investigation within management pack XML to figure out why the numbers weren’t adding up the way they “should be”. The short version is this: remember up above, where Technet called out two group types, Computer Groups and Instance Groups? It turns out that information is incomplete, and there are a few contributing factors to our odd results above.

 

First, let’s go back to talking about classes. Both the Microsoft.SystemCenter.InstanceGroup and Microsoft.SystemCenter.ComputerGroup classes derive from a common base class, “System.Group”. There is another group type used which descends from System.Group called “Microsoft.SystemCenter.FunctionalInstanceContainer”, which is used as the base class for several groups which ship with Operations Manager. Additionally, there are several groups both out of the box and in Microsoft management packs which derive directly from System.Group, skipping the Instance Group and Computer Group level entirely. Making matters worse still, the Virtual Machine Manager management packs seemed to disregard the established group class structure entirely, instead creating a whole chain of custom group classes deriving from System.Group and each other. The only consistent rule I could determine is that everything will derive from System.Group at some point.

 

We can update the Where-Object criteria in the PowerShell commands above to return any class where System.Group is a base type by calling the GetBaseTypes method followed by the Contains method. However, if we don’t account for it, the System.Entity base class (from which all other classes derive) will cause a terminating error to be generated when processing, as the Contains method can’t be called on a null value. Taking that into account, our restructured foreach command is this:

$MPGroupClasses = @()
foreach ($MP in $MPs) {
    $MPClasses = $MP.GetClasses()
    If ($MPClasses.count -gt 0) {
        foreach ($Class in $MPClasses) {
            If (($Class.Name -ne "System.Entity") -and (($Class.GetBaseTypes()).Name.Contains("System.Group"))) {
                $MPGroupClasses += $Class
            }
        }
    }
}

 

If we again compare our counts of objects returned as above, we’ll see things still aren’t right:

 

10 - Better Groups From MP

 

Coming at it from the management pack side is resulting in a far higher number of SCOM groups being returned than via the Get-SCOMGroup cmdlet. Additionally, we are still seeing less group objects available when we use Get-SCOMGroup with the IDs from our management pack group classes.

 

Fortunately, the answers to these problems show that they aren’t nearly as much of a problem as they look.

 

First, using Compare-Object, we can get the list of management pack classes which are being returned by our query but for which no group objects are being returned:

 

11 - Missing Classes

 

Checking into these classes by using Get-SCOMClassInstance or by looking up their DisplayNames in the console (via the Groups or Discovered Inventory views) shows pretty clearly that, while these classes exist in their management packs, no class instances exist. Therefore, it is no wonder that no groups were returned; there are none to return and this is simply extra data collected by our method but that can be safely discarded. (I should note that, in my example environment, most of these groups are contained in Microsoft management packs, but only a small handful are out of the box. The number of these groups in your environment will vary, but they can still be safely ignored.)

 

Now the only irregularity remaining is the difference in number between the groups returned from Get-SCOMGroup without filters but aren’t being returned when we filter off our management pack class Ids. If we use Compare-Object again, we’ll see the following results:

 

12 - Groups Only from Cmdlet

 

Further investigation into these groups reveals that they fall into one of two categories:

  1. They are an Agent Watcher group created from a custom Web Application monitoring template
  2. They are a built-in group that cannot be altered

 

For the first type, we don’t really care about them when we are talking about Groups anyway and they aren’t visible from the Groups view in the Operations Console.

 

For the second type, only three of them are visible from the Groups view in the Operations Console, but their membership will always be a 1:1 match with the number of agents, management servers, and gateway servers in your environment, respectively.

 

Therefore, all of those can also be safely ignored and our method of getting a list of SCOM groups contained in a list of management packs works splendidly. Here is the complete script text to do so:

$MPs = Get-SCOMManagementPack (add additional criteria, where-object pipes, etc. here)
$MPGroupClasses = @()
foreach ($MP in $MPs) {
    $MPClasses = $MP.GetClasses()
    If ($MPClasses.count -gt 0) {
        foreach ($Class in $MPClasses) {
            If (($Class.Name -ne "System.Entity") -and (($Class.GetBaseTypes()).Name.Contains("System.Group"))) {
                $MPGroupClasses += $Class
            }
        }
    }
}
$GroupsByMP = Get-SCOMGroup -Id $MPGroupClasses.Id

 

That’s all I’ve got for right now. Hopefully, this dive into SCOM Groups with PowerShell has been interesting and informative. If anyone has any additional questions, feel free to contact us and let us know.

 

Article By Gabriel Taylor
With over 12 years of experience in the IT industry, Gabriel brings a focus on repeatable processes, solution design, and quality execution to Model’s Project Services practice. He believes the true value of technology is how it enables businesses to gain efficiencies, increase productivity, and achieve their goals. He is proud to work with Model’s team of experts to bring those benefits to Model’s clients.

Related Posts