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.
If you too were looking for a reliable and easy way to do this, well, look no further! I have created an MP Fragment in my fragment library for this (Class.Group.ADGroupWindowsComputers.mpx)
Download my fragment library here:
https://github.com/thekevinholman/FragmentLibrary
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 Studio with 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 relationship to the existing Windows Computers in SCOM)
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.6 #================================================================================= 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##.ADBased.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:\SOFTWARE\Microsoft\System Center Operations Manager\12\Setup\Powershell\V2" $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).") # Health Service class section # We need this list of SCOM agents, so we can only submit discovery data for a Healthservice in SCOM otherwise SCOM will reject the discovery data, and this will clean up deleted stale Windows Computer objects that will remain until the next discovery # Clear any previous errors $Error.Clear() # Get all instances of a existing Health Service class $HS = Get-SCOMClass -Name "Microsoft.SystemCenter.Healthservice" | Get-SCOMClassInstance $HSNames = $HS.DisplayName $HSCount = $HSNames.count IF($Error) { $momapi.LogScriptEvent($ScriptName,$EventID,1, "`n FATAL ERROR: Unable to gather Healthservice instances from SCOM. `n Error is: $Error") EXIT } ELSE { $momapi.LogScriptEvent($ScriptName,$EventID,0, "`n Get all Health Service Objects from SCOM has completed. `n Returned ($HSCount) Health Service Objects from SCOM.") } # END Health Service class section # 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() IF ($Error) { $momapi.LogScriptEvent($ScriptName,$EventID,1,"`n FATAL ERROR: Failure getting data from AD for ($ADGroup). `n Terminating script. `n Error is: ($Error).") EXIT } $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. `n ADGroup: ($ADGroup) `n GroupDN: ($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 = New-Object DirectoryServices.DirectorySearcher $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. `n ADGroup: ($ADGroup) `n GroupDN: ($GroupDN).") } Else { $momapi.LogScriptEvent($ScriptName,$EventID,1, "`n FATAL ERROR: Did not find any members in the AD group. `n ADGroup: ($ADGroup) `n GroupDN: ($GroupDN). `n Terminating script.") EXIT } # Set namelist array to empty $NameList = @() # Loop through each computer object and build an array of FQDN hostnames FOREACH ($ADComputerObject in $ADComputerObjects) { #Get the DNS Hostname property from AD [string]$DNSComputerName = $ADComputerObject.Properties.dnshostname #Only add the name to the array if the DNSHostname property exists in AD IF ($DNSComputerName) { $NameList += $DNSComputerName } } $NameListCount = $NameList.Count IF ($NameListCount -ge 1) { $momapi.LogScriptEvent($ScriptName,$EventID,0,"`n Successfully found ($NameListCount) DNS Names from the original ($ADComputerObjectsCount) members in the group. `n ADGroup: ($ADGroup) `n GroupDN: ($GroupDN).") } ELSE { $momapi.LogScriptEvent($ScriptName,$EventID,1,"`n FATAL ERROR: There was an error getting DNS Host Names or none were found for members of the group. `n Group objects count: ($ADComputerObjectsCount). `n ADGroup: ($ADGroup) `n GroupDN: ($GroupDN). `n Terminating script.") EXIT } #Discovery Section #Set the group instance we will discover members of $GroupInstance = $DiscoveryData.CreateClassInstance("$MPElement[Name='##CompanyID##.##AppName##.##GroupNameNoSpaces##.ADBased.Group']$") # Loop through each SCOM computer and add a group membership containment relationship to the discovery data $i=0; FOREACH ($Name in $NameList) { #Check to make sure the name we got from AD exists as a Healthservice in this Management Group IF ($Name -in $HSNames) { $i = $i+1 $ServerInstance = $DiscoveryData.CreateClassInstance("$MPElement[Name='Windows!Microsoft.Windows.Computer']$") $ServerInstance.AddProperty("$MPElement[Name='Windows!Microsoft.Windows.Computer']/PrincipalName$", $Name) $RelationshipInstance = $DiscoveryData.CreateRelationshipInstance("$MPElement[Name='##CompanyID##.##AppName##.##GroupNameNoSpaces##.ADBased.Group.Contains.Windows.Computers']$") $RelationshipInstance.Source = $GroupInstance $RelationshipInstance.Target = $ServerInstance $DiscoveryData.AddInstance($RelationshipInstance) } } IF ($i -ge 1) { $momapi.LogScriptEvent($ScriptName,$EventID,0,"`n Successfully found ($i) Computers in SCOM from the original ($NameListCount) DNS names pulled from the group. `n ADGroup: ($ADGroup) `n GroupDN: ($GroupDN).") } ELSE { $momapi.LogScriptEvent($ScriptName,$EventID,1,"`n FATAL ERROR: No computers in SCOM were found matching the ($NameListCount) DNS names pulled from the group. `n Group objects: ($ADComputerObjectsCount). `n ADGroup: ($ADGroup) `n GroupDN: ($GroupDN). `n Terminating script.") EXIT } # 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.") #=================================================================================
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.
Kevin,
Love this fragment, and use it often! Thanks. One question: we monitor multiple domains (untrusted) in a single SCOM Management Group; and this script runs under the default SCOM credentials. There are alternate action accounts defined in the MG to access the other domains; is there a way to run the script with different credentials / action account?
You cannot run a workflow in SCOM using an untrusted RunAs account. When you configure RunAs, we create a monitoringhost.exe process AS a specific user credential, and then run all workflows under that process. An untrusted account cannot log on or be authenticated. To solve your issue, you’d have to come up with some way to have an agent or GW in the untrusted forest run the AD queries, then have a way to use that output as the group population datasource.
Kevin,
Thanks for the response… drat, I was afraid of that. Looks like the gateway is the way to go… another challenge ahead!
Peter
Hi Kevin, I can created a MP for find AD computers and the MP runs without errors and state we found this amount however I got no group members into the SCOM group – do you know what might be causing this issue ?
I’d look at the events that the script logs, then look for other events about SCOM rejecting the discovery data.
Thanks Kevin, looks like the powershell is timing out – so we are tightening the LDAP string as we was going to the top level domain
Hi Kevin, I’ve used this method for creating a group, and it initially creates and populates the group as expected. The problem I’m having is when the source – AD group/file/whatever – changes, the group membership doesn’t change. Any thoughts what might be happening there? Cheers, Steve
If you look at the events logged by the script – does it show what should be discovered correctly? If so – do you see events on the same SCOM management server rejecting the discovery data?
I didn’t include the logging in the script. I know, an oversight on my part. I’ve tested the script externally to the MP and it’s definitely find the correct objects to add to the group. There were no events on the management server indicating an issue with the discovery – I know the events you are referring to there.
Now interestingly, in an attempt to make troubleshooting easier, I reimported the MP with the only change being the interval the script runs – down from once a day to every 10 minutes. A few minutes after this the new object was added to the group. The change to the AD group happened 3 days ago, so the script would definitely have run in that time.
I’ll add logging to the script and see if I can replicate the issue.
Hi Kevin,
Love your work 🙂
Do you have a step by step guide for creating a SCOM group from an Active Directory Computer Group using Visual Studio?
Thanks
Hi Kevin,
I have run through this but I am seeing an error in my SCOM logs for the discovery. Do you have any suggestions what I may have done incorrectly?
SecuritasUSA.AllDevServers.AllDevServers.ADBased.Group.Discovery.ps1 :
FATAL ERROR: Failure getting data from AD for (All Development Servers).
Terminating script.
Error is: (Exception calling “FindAll” with “0” argument(s): “Unspecified error
“).
Resolved my issue on last item. Does this support LDAPS?
what is the point of the $SourceId, $ManagedEntityId and how are you passing them to the script in the MP? or are they only for running it outside of scom?
Hi Kevin, thank you for the great and handy Management Pack! Just one question: The AD Objects were discovered by the MP, but it generates some Eventlog Errors: Forced to terminate the following PowerShell script because it ran past the configured timeout 300 seconds.
That’s BAD! This should not take long to run. I’d change the script and add more logging, or try and figure out running it manually what part is taking so long. Are there a large number of computers in the group?
Hi Kevin, according to your answer to Rene you said that you have updated the script. On the github Repo I can only find version 1.6 (7 months ago uploaded). I am asking because I have the same problem like Rene, but there are only 4 computers in the AD group.
Thanks in Advance!
Best Regards
Thomas
No – I didn’t change mine – I said I recommend adding more logging to see where the slowdown is – or step through it manually and see what is taking so long.
Hi Kevin, cant seem to get the script. Could you confirm if this will work on SCOM 2019? trying to do this exact same thing.. I figured it would just work.. alas it does not
Works fine on SCOM 2019. No difference. I updated the download link to the fragments.
Pingback:Control your discovery for Group in SCOM - BLOG of SCOM
Hi Kevin,
We have a universal group that contains computers account (Windows servers) from some child domains.
the LDAP:// path that we put was the path of the forest. unfortunately only object that was under the main domain was entered to the group, and the others object that was in another child domains was not allocated.
Have you encountered this type of problem ?
can I insert the GC:\\ syntax for query the global catalog and maybe that will help ?
Thanks for the info, but this is totally bogus! To put it nicely. I have to download some software that I dont have a license for, import the fragment into a new MP, then import the MP etc. all to get group memberships to sync from AD?!! Lame! This is a MSFT product that connects to AD with MPs and yet I have to jump through these hoops to get group memberships. This causes me to start looking at other products. One more strike as it is called. Thanks for the info though, but I will not be doing this.
Hi PJ. You don’t have to use Silect – they just make fragments super easy. The standard tool for all MP authoring in SCOM is Visual Studio with VSAE – that’s free. You can pretty easily do that as well. The only thing that could make this simple is a UI for making SCOM groups from AD groups – but I can tell you – less than 10% of our customers even bother doing that. This is not all that common of a request. If I can help further, let me know.
Hi Kevin. Your fragments are of great help even for us that’s not so experienced in programming. I’ve lately used the fragment Class.Group.ADGroupWindowsComputers.mpx to create SCOM groups from AD, and it works as it should.
Now we need to add Health Service Watchers for the same computer groups created with this fragment. Do you have some recommendations how to achieve this? If I need to edit the fragment, it would be nice to know some details about what should be added. Or are there any other way to do it?
Great comment and request. Check my latest fragments – I uploaded one that does this for ya.
https://github.com/thekevinholman/FragmentLibrary
Thanks a lot for the new fragment and the swift response. It worked like a charm and finally we now can retire our old and not-so-good working management pack that more or less did the same.
Got error when getting group from AD:
CF.Group.CF_testgroup.ADBased.Group.Discovery.ps1 :
FATAL ERROR: Failure getting data from AD for (Test group).
Terminating script.
Error is: (Cannot convert value “))” to type “System.Int32”. Error: “Input string was not in a correct format.”).
When running it on the server with errors on action account all looks good no errros :/
Issue solved
Hi Kevin,
I am really stuck with this. I have tried to go through your blog about how to use VSAE as the MP Author application is no longer available to download.
I have no idea what I am doing wrong. I create a Groups folder and imported the mpx file.
I then updated the values that needed changing. When I try to build it is throws about 10 errors stating the the ID attribute is invalid according to the datatype ManagementPackUniqueIdentifier.
Completely stumped. Would be great if you could help me out.
Thanks
Please ignore … I literally just figured it out now.
The issue is that the “Group Name” and the “Group Name without spaces” are exactly the same for us. So I just made up a “Group Name with no spaces” as I assume you are using that as the Unique ID.
Hello Kevin. Love this fragment, thank you very much. One question though. Let’s say I want to discover multiple groups. Would you advise just adding and editing this fragment multiple times? Because it looks like a new class instance and discovery type is created for every fragment and I’m not sure that is the intention.
Thanks.
forgive my ignorance, but for the this MP, i have to update the fragment and deploy for every AD group i want to find?
Yes, the way this fragment is designed.
When I run this from another but trusted domain, the script fails to enumerate the group members. It says it finds the group, but cant find the members, giving a FATAL ERROR.
get-adgroupmember will run if i add the -server parameter.
Any idea what i need to add to ldapsearch path to have it look in another domain ?