Thursday, March 12, 2015

(Sort of) Dynamic Security Groups in Active Directory

Working with a customer who is migrating from Novell to Windows, I was asked if Active Directory has dynamic groups. In eDirectory, you can create a group whose members are determined by the result of an LDAP query. Active Directory doesn't have this functionality. There are dynamic distribution groups with Exchange, but those aren't security groups.

I'm not a Powershell guru by any means, but I created a small Powershell script that would perform a query, remove the members of a security group, then replace the members with the results of the query. Over lunch, my wife asked, "What if I logon at the exact moment the group membership has been cleared?", which led me to make some changes. The script now creates two arrays - one containing the current group members, the other containing the results of the query. I then compare the contents of the arrays, removing current group members that aren't in the query results, and adding group members from the query results that aren't already in the group. That way, anyone currently in the group who are also in the query results is left alone.

Because Powershell works with objects and is pretty smart about it, I first tried comparing the entire array to the other array member with "If (-Not ($current.Contains($new[$i])))", but that didn't work - the If statement was never true. I found that it does work if I specify the name property, as shown below. Note that the first element of an array is 0 (zero), so an array with 20 values will have element 0 to 19.

Here is the final script:

$current = @(Get-ADGroupMember groupname)
$new = @(Get-ADUser -Filter {Name -like "*"} -SearchBase "ou=Users,dc=your,dc=domain" -SearchScope Subtree)

For ($i=0; $i -lt $new.count; $i++) {
   If (-Not ($current.name.Contains($new[$i].name))) {
   Add-ADGroupMember groupname -member $new[$i] -Confirm:$false
   }
}

For ($i=0; $i -lt $current.count; $i++) {
   If (-Not ($new.name.Contains($current[$i].name))) {
   Remove-ADGroupMember groupname -member $current[$i]
   -Confirm:$false
   }
}

Replace the group name and OU path with your own. It selects all users in or under the OU path, but you could have any filter you wish. For example, you could use {Surname -Like "Smith"} if you wanted to find all the Smiths in your organization.

Lastly, create a scheduled task to run this on a regular basis. Every 30 or 60 minutes is probably adequate. It's not a dynamic group, but it's pretty close.