There have been a bunch of examples of this published over the years. Some of them worked well, but I was never happy with many of them as they were often vbscript based, hard to troubleshoot, and required lots of editing each time you wanted to reuse them. Many were often error prone, and didn’t work if the AD group contained computers that didn’t exist in SCOM, as SCOM will reject the entire discovery data payload in that case.
I have created an MP Fragment in my fragment library for this:
https://gallery.technet.microsoft.com/SCOM-Management-Pack-VSAE-2c506737
This MP Fragment will make creating SCOM groups of Windows Computers from Active Directory groups super easy! This is a nice way to “delegate” the ability for end users to control what servers will appear in their scopes, as they often have the ability to easily add and remove computers from their AD groups, but they do not have access to SCOM Group memberships.
I am going to demonstrate using Silect MP Author Pro to reuse this Fragment, and you can also easily use Visual Studio with VSAE. If you’d like to read more on either of those, see:
In Silect MP Author Pro – create a new, empty management pack, and select “Import Fragment”
Browse the fragment and choose: Class.Group.ADGroupWindowsComputers.mpx
We need to simply input the values here, such as:
Click “Import”
Silect MP Author Pro will automagically handle the references for you, so just say “Yes” on the popup:
That’s IT!
Save it, and deploy it!
If you look in SCOM after a few minutes – you should see your group:
The rule to populate it runs once a day by default, but it will run immediately upon import. Look for event ID 7500 in the OpsMgr event log on the Management Server that hosts your All Management Servers Resource Pool object
Once you see these events and no errors in them – you can view group membership in SCOM:
So easy. And you don’t have to know anything about XML, or even Management Packs to do it!
Using Visual and VSAE works exactly the same way – you simply have to do a manual Find/Replace for each item. See the VSAE method in the link above.
Want to dig deeper into how this is put together? Read on:
The MP we generate is very basic. There is a Class (the Group definition) a Relationship (the Group contains Windows Computers) and a discovery (queries AD and discovers the group members)
The script is below:
We basically connect to AD, find the group by name, query to get members, look the membership up and see if they exist in SCOM, if they do, add them to the group.
We will log events along the way to help in troubleshooting if anything doesn’t work, and record the completion and total script runtime, like all my SCOM scripts.
#================================================================================= # Group Population script based on AD group membership # # Kevin Holman # v1.2 #================================================================================= param($SourceID, $ManagedEntityID, $ADGroup, $LDAPSearchPath) # Manual Testing section - put stuff here for manually testing script - typically parameters: #================================================================================= # $SourceId = '{00000000-0000-0000-0000-000000000000}' # $ManagedEntityId = '{00000000-0000-0000-0000-000000000000}' # $ADGroup = "SCOM Computers Group" # $LDAPSearchPath = "LDAP://DC=opsmgr,DC=net" #================================================================================= # Constants section - modify stuff here: #================================================================================= # Assign script name variable for use in event logging $ScriptName = "##CompanyID##.##AppName##.##GroupNameNoSpaces##.Group.Discovery.ps1" $EventID = "7500" #================================================================================= # Starting Script section - All scripts get this #================================================================================= # Gather the start time of the script $StartTime = Get-Date # Load MOMScript API $momapi = New-Object -comObject MOM.ScriptAPI # Load SCOM Discovery module $DiscoveryData = $momapi.CreateDiscoveryData(0, $SourceId, $ManagedEntityId) #Set variables to be used in logging events $whoami = whoami #Log script event that we are starting task $momapi.LogScriptEvent($ScriptName,$EventID,0,"`n Script is starting. `n Running as ($whoami).") #================================================================================= # Connect to local SCOM Management Group Section #================================================================================= # Clear any previous errors $Error.Clear() # Import the OperationsManager module and connect to the management group $SCOMPowerShellKey = "HKLM:SOFTWAREMicrosoftSystem Center Operations Manager12SetupPowershellV2" $SCOMModulePath = Join-Path (Get-ItemProperty $SCOMPowerShellKey).InstallDirectory "OperationsManager" Import-module $SCOMModulePath New-DefaultManagementGroupConnection "localhost" IF ($Error) { $momapi.LogScriptEvent($ScriptName,$EventID,1,"`n FATAL ERROR: Failure loading OperationsManager module or unable to connect to the management server. `n Terminating script. `n Error is: ($Error).") EXIT } #================================================================================= # Begin MAIN script section #================================================================================= #Log event for captured parameters $momapi.LogScriptEvent($ScriptName,$EventID,0,"`n ADGroup: ($ADGroup) `n LDAP search path: ($LDAPSearchPath).") # Connect to AD using LDAP search to find the DN for the Group $Searcher = New-Object DirectoryServices.DirectorySearcher $Searcher.Filter = '(&(objectCategory=group)(cn=' + $ADGroup + '))' $Searcher.SearchRoot = $LDAPSearchPath $Group = $Searcher.FindAll() $GroupDN = @() # Now that we have the group object, trim to get the DN in order to search for members $GroupDN = $Group.path.TrimStart("LDAP://") #If we found the group in AD by the DisplayName log a success event otherwise log error IF ($GroupDN) { $momapi.LogScriptEvent($ScriptName,$EventID,0,"`n Successfully found group in AD: ($GroupDN).") } ELSE { $momapi.LogScriptEvent($ScriptName,$EventID,1,"`n FATAL ERROR: Did not find group in AD: ($ADGroup) using ($LDAPSearchPath). `n Terminating script.") EXIT } # Search for members of the group $Searcher.Filter = '(&(objectCategory=computer)(memberOf=' + $GroupDN + '))' $ADComputerObjects = $Searcher.FindAll() $ADComputerObjectsCount = $ADComputerObjects.Count If ($ADComputerObjectsCount -gt 0) { $momapi.LogScriptEvent($ScriptName,$EventID,0,"`n Successfully found ($ADComputerObjectsCount) members in the group: ($GroupDN).") } Else { $momapi.LogScriptEvent($ScriptName,$EventID,1, "`n FATAL ERROR: Did not find any members in the AD group: ($GroupDN). `n Terminating script.") EXIT } # Set namelist array to empty $namelist = @() # Loop through each computer object and get an array of FQDN hostnames FOREACH ($ADComputerObject in $ADComputerObjects) { [string]$DNSComputerName = $ADComputerObject.Properties.dnshostname $namelist += $DNSComputerName } # Check SCOM and get back any matching computers # This is necesasary to filter the list for relationship discovery because if we return any computers missing from SCOM the Management Server will reject the discovery # We are using the namelist array of FQDNs passed to Get-SCOMClassInstance to only pull back matching systems from the SDK as opposed to getting all Windows Computers then parsing which is assumed slower in large environments $ComputersInSCOM = Get-SCOMClassInstance -Name $namelist $ComputersInSCOMCount = $ComputersInSCOM.Count # Logging event $momapi.LogScriptEvent($ScriptName,$EventID,0,"`n Found ($ComputersInSCOMCount) matching computers in SCOM from the ($ADComputerObjectsCount) total computers in the AD group ($GroupDN).") #Discovery Section #Set the group instance we will discover members of $GroupInstance = $DiscoveryData.CreateClassInstance("$MPElement[Name='##CompanyID##.##AppName##.##GroupNameNoSpaces##.Group']$") # Loop through each SCOM computer and add a group membership containment relationship to the discovery data FOREACH ($ComputerInSCOM in $ComputersInSCOM.DisplayName) { $ServerInstance = $DiscoveryData.CreateClassInstance("$MPElement[Name='Windows!Microsoft.Windows.Computer']$") $ServerInstance.AddProperty("$MPElement[Name='Windows!Microsoft.Windows.Computer']/PrincipalName$", $ComputerInSCOM) $RelationshipInstance = $DiscoveryData.CreateRelationshipInstance("$MPElement[Name='##CompanyID##.##AppName##.##GroupNameNoSpaces##.Group.Contains.Windows.Computers']$") $RelationshipInstance.Source = $GroupInstance $RelationshipInstance.Target = $ServerInstance $DiscoveryData.AddInstance($RelationshipInstance) } # Return Discovery Items Normally $DiscoveryData # Return Discovery Bag to the command line for testing (does not work from ISE) # $momapi.Return($DiscoveryData) #================================================================================= # End MAIN script section # End of script section #================================================================================= #Log an event for script ending and total execution time. $EndTime = Get-Date $ScriptTime = ($EndTime - $StartTime).TotalSeconds $momapi.LogScriptEvent($ScriptName,$EventID,0,"`n Script Ending. `n Script Runtime: ($ScriptTime) seconds.") #================================================================================= #End Script
Key recommendations:
1. Don’t run your frequency <intervalseconds> too often. If updating the group once a day is ok, leave it at the default. If you need it more frequent, that’s fine, just remember it’s a script, and all scripts running on the management servers have an impact on the overall load, plus we are submitting discovery data about relationships each time, and searching through AD.
2. The default timeout is set to 5 minutes. If you cannot complete this in less, something is WRONG and it most likely will be how long it takes to find the group in AD. If that is true for you, you need to optimize the section on querying AD and LDAP search path.
3. If you have a lot of AD based SCOM groups, consider adding a staggered sync time to each discovery, so they don’t all run at the same time, or on the same interval.