Programmatic Group Membership Manipulation in SCOM 2007

Someone might ask: why the heck do I need that? And yes, I never thought that I will need it but I learned that in SCOM 2007 a group is not always a group.

Jakub Oleksy helped me to understand and solve my problem and I am happy to share this information with you.

The problem is, that groups created by the web application wizard can not be tweaked using the UI in SCOM 2007. It’s not possible to create a dynamic membership rule. It’s also not possible to assign a subgroup to such a group. So having a lot of servers and a lot of web applications it’s not really fun to pick every single server in every single web application.

Here my workaround:

  • Create a web application using the wizard
    A group named after your web application with the suffix “watcher computers group” will be created
  • Create your own computer group using the UI and define a dynamic membership rule
  • Configure the script (the 3 lines at the beginning) and test (DO NOT RUN THIS UNTESTED IN PRODUCTION ENVIRONMENT!)

This is just a proof-of-concept script to show how you can do stuff like this. According to Jakub there may be a fix in SP1 which enables you to utilize the group created by the web application wizard in a way to either create a dynamic membership rule or attaching a subgroup, or both.

Disclaimer:
USE THIS AT YOUR OWN RISK. I AM NOT RESPONSIBLE FOR ANY DAMAGE THIS SCRIPT MAY CAUSE. THAT SAID, HAVE FUN!

Here’s the script:

$ManagementServer = ServerNameHere!
$SourceGroupID = GetComputerGroupIDFromDisplayName(DisplayName of SourceGroup)
$DestinationGroupID = GetComputerGroupIDFromDisplayName(WebAppName watcher computers group)

if (CompareComputerGroupMembership($SourceGroupID, $DestinationGroupID) -eq 0)
{
    SyncComputerGroupMembership($SourceGroupID, $DestinationGroupID, $ManagementServer)
}

function SyncComputerGroupMembership([string] $SourceGroupID, [string] $DestinationGroupID, [string] $ManagmentServer)
{
    ## load SDK assemblies
    [System.Reflection.Assembly]LoadWithPartialName(Microsoft.EnterpriseManagement)
    [System.Reflection.Assembly]LoadWithPartialName(Microsoft.EnterpriseManagement.Configuration)
    [System.Reflection.Assembly]LoadWithPartialName(Microsoft.EnterpriseManagement.ConnectorFramework)
    [System.Reflection.Assembly]LoadWithPartialName(Microsoft.EnterpriseManagement.Monitoring)
    
    ## connect to management group
    $ManagementGroup = New-Object Microsoft.EnterpriseManagement.ManagementGroup($ManagementServer)

    ## build include list xml items from source group childs
    $IncludeListXML = 
    $SourceGroupChilds = Get-ChildItem (Get-MonitoringClass  where {$_.Name -eq $SourceGroupID})  Sort DisplayName
    foreach ($SourceGroupChild in $SourceGroupChilds) { $IncludeListXML = $IncludeListXML + ( + $SourceGroupChild.Id.ToString() + )}

    ## get destination group's discovery configuration
    $DestinationGroup = $ManagementGroup.GetMonitoringClasses($DestinationGroupID)[0]
    $Discovery = $DestinationGroup.GetMonitoringDiscoveries()[0]
    $DiscoveryConfiguration = $DestinationGroup.GetMonitoringDiscoveries()[0].DataSource.Configuration

    ## create new discovery configuration
    $NewConfiguration = 
    if ($DiscoveryConfiguration.IndexOf() -lt 0)
    {
        ## include list present
        $Start = $DiscoveryConfiguration.IndexOf() + 17
        $NewConfiguration = '$MPElement[Name=Windows!Microsoft.Windows.Computer]$MPElement[Name=SC!Microsoft.SystemCenter.ComputerGroupContainsComputer]

        $NewConfiguration = $NewConfiguration + $IncludeListXML + 
        $NewConfiguration = $DiscoveryConfiguration.Substring(0, $Start) + $NewConfiguration + $DiscoveryConfiguration.Substring($Start)
    }
    else
    {
        ## include list not present
        $Start = $DiscoveryConfiguration.IndexOf() + 13
        $End = $DiscoveryConfiguration.IndexOf()
        $NewConfiguration = $DiscoveryConfiguration.Substring(0, $Start) + $IncludeListXML + $DiscoveryConfiguration.Substring($End)
    } 
    

    ## set new discovery configuration
    $Discovery.Status = [Microsoft.EnterpriseManagement.Configuration.ManagementPackElementStatus]PendingUpdate
    $Discovery.DataSource.Configuration = $NewConfiguration
    $Discovery.GetManagementPack().AcceptChanges()
    
}

function GetComputerGroupIDFromDisplayName([string] $GroupDisplayName)
{
    ## returns the ID (name property) of the computer group specified by display name
    return (Get-MonitoringClass  where {$_.DisplayName -eq $GroupDisplayName}).Name
}

function CompareComputerGroupMembership([string] $GroupID1, [string] $GroupID2)
{
    ## use this function to compare the membership of 2 computer groups
    ## 2 parameters, IDs of the groups to compare
    ## returns 1 if the membership of the groups is identical, otherwise 0
    
    $GroupID1Members = Get-ChildItem (Get-MonitoringClass  where {$_.Name -eq $GroupID1})  Sort Id
    $GroupID2Members = Get-ChildItem (Get-MonitoringClass  where {$_.Name -eq $GroupID2})  Sort Id
    
    $GroupID1MembersString = 
    foreach ($Member1 in $GroupID1Members) { $GroupID1MembersString = $GroupID1MembersString +   + $Member1.Id.ToString() }

    $GroupID2MembersString = 
    foreach ($Member2 in $GroupID2Members) { $GroupID2MembersString = $GroupID2MembersString +   + $Member2.Id.ToString() }

    if ($GroupID1MembersString -eq $GroupID2MembersString) { return 1 }    else { return 0 }
    
}

Leave a Reply