We where talking to a customer about how to avoid waiting for Active Directory group synchronization to occur and place a device in the correct collections faster than “until the next synchronization”.
The main problem with this setup was caused by the fact that they used a group-in-group membership to identify collection memberships and apparently SCCM 2012 don’t include indirect changes to group membership as delta changes (I have not tested this in details yet).
So we came up with the idea to just create a direct membership to place the device in the collections instantly to make sure that it was there when the first policy is loaded on the client.
While that would work for cutting the synchronization time down to near nothing, it creates a new problem, any changes made to the group membership would not affect the collection correctly as a member would not be removed if a direct membership is still in place. So to fix this we need a process to clean up this mess.
The solution was fairly simple to do using a PowerShell script
Please note that the script is meant to run on the server using current credentials. The script was tested on SCCM 2012 SP1 and with PowerShell 3.0 and is provided as is, any comments and input are welcome.
Now let’s start coding …
First let’s create a function to and make the script simple to call using a few arguments.
PARAM( [Parameter(Mandatory=$True,HelpMessage="Please Enter Site Server Site code")] $SiteCode, [Parameter(Mandatory=$True,HelpMessage="Please Enter CollectionID")] $CollectionID ) function CleanupDirectMembershipsInCollection($SiteCode, $CollectionID) {
Next we need to get the collection object from SCCM. Then we create two arrays for later use. Next we need to find all direct members of the collection, we store these in the $directmembers variable.
# Get collection from WMI $coll = Get-WmiObject -Namespace root\sms\site_$SiteCode -Class sms_collection -Filter "CollectionID = '$CollectionID'" # Continue if we got a collection if ($coll) { Write-Host "Found collection $($coll.Name)" # Load lazy properties $coll.get() # Create arrays for holding members $members = @() $directmembers = @() # Get all ResourceIDs that are member by a DirectRule $directmembers += Get-WmiObject -Namespace root\sms\site_$SiteCode -Query "SELECT * FROM SMS_CollectionMember_A WHERE CollectionID = '$CollectionID'" | Where IsDirect -eq $True | Select ResourceID Write-Host "Found $($directmembers.count) direct members"
Before doing a lot of work we check to see if there was any directmembers, if not there is no reason to continue this process.
# Continue if there are any direct members if ($directmembers.Count -gt 0) {
The we loop through all rules that contains a query. The query expression is run directly using WMI to get the members.
Any resources found using the query is then added to the $members array (if not already there).
# Get all collection rules foreach ($item in $coll.CollectionRules) { # Continue if the rule is a query based rule if ($item.__CLASS -eq "SMS_CollectionRuleQuery") { # Get the query $query = $item.queryExpression Write-Host "Found query rule called $($item.RuleName)" # Get the result of the query from SCCM $result = Get-WmiObject -Namespace root\sms\site_$SiteCode -Query $query Write-Host "Got result of query" # Loop through all resources in the result foreach ($resource in $result) { # If the resourceID is not yet in the members array if ($members -notcontains $resource.ResourceId) { # Add it to the array $members += $resource.ResourceID } } } } # Summary for the query based rules Write-Host "Found $($members.count) members using queries"
Now that we have all directmembers and all members from any queries, we can do the cleanup.
We loop through all the direct rules and check if the referenced resource is found in the members array (that contains all members from any query)
If we find the resource, we can safely delete the direct rule from the collection.
# Loop through all direct rules foreach ($item in $coll.CollectionRules) { if ($item.__CLASS -eq "SMS_CollectionRuleDirect") { Write-Host "Found direct rule for $($Item.ResourceID)" # If the ResourceID is found in members if ($members -contains $item.ResourceID) { # Remove the direct rule for this resourceID $coll.DeleteMembershipRule($item) | Out-Null Write-Host "DirectRule for $($member.ResourceID) was deleted" } } }
Finally we request a membership refresh on the collection so that SCCM is in sync.
# Finally request a refresh of the Collection just for good measure $Coll.RequestRefresh($true) | Out-Null write-host "Colletion refresh for $($Coll.Name) was requested"
The last bit we need is to call the function using the arguments supplied
CleanupDirectMembershipsInCollection -SiteCode $SiteCode -CollectionID $CollectionID
That’s it folks, now the collection only contains members that are either query-based or direct members and not both.
To call the script using a syntax like this, you will of cause have to replace sitecode and collectionid with your values.
collectioncleanup.ps1 –sitecode pri –collectionid PRI00032
The complete PowerShell script can be downloaded from the link below
[download id=”136″ format=”1″]
Hi Kent,
nice work with WMI!
I wrote a script just a week ago doing the exact same thing with the native SP1 cmdlet
http://www.david-obrien.net/2013/02/24/remove-direct-membership-rules-configmgr/
But as I said several times the last days: I still like the WMI approach more, because of error handling and output.
Hi David, sorry I am not the Kent you might think I am , my name is infact not even Kent, It’s Ronnie :-), but your comments are still very welcome.
I took a quick look at your script and there is a small different compared to my script. Your script removes all direct memberships from a Collection, whereas mine only removes direct memberships for ressources that are also member by one or more queries.
Hi Ronnie
Your scripting is simply gr8!.
i am in similer need, but i am not into powershell , will you able to help me?
1. Obtain the list of “All Users” collection that have been defined as a Primary User of a device, and what that Users ShortName and Device name is
2. Obtain the list of user from Active Directory that have their “Title” attribute equal to “Non-Employee” (samAccountName)
3. For each user that is returned from AD, determine if they are assigned as a Primary User of a Device and write the Device name to a file
4. Continue to append all of the applicable Device names to the file
5. End Result = List of all Devices that have Users that have their AD Attribute “Title” equal to “Non-Employee”