Tag Archives: Security Groups

Active Directory Architecture: How to Search for old Security Groups

Recently I was asked to get a grasp on an organizations Active Directory and make any recommendations on how to clean it up or improve upon its structure.

Task #1 for me was getting a handle on the security groups in place. Security (and Distribution) Groups are often neglected. They are set up by someone, somewhere, sometime and then typically forgotten. As time passes and users are copied to create new users, these security groups can become security holes as rights are given to people unintentionally.

For this particular organization, this was no easy challenge as they had thousands of undocumented groups. Where would I ever even begin to start?

I was fortunate in the fact that this particular organization does not copy users to create new users. All users are created from scratch and then added to the appropriate groups on an as needed basis (which, by the way, is a good security principle to follow). Since we know that Microsoft Windows Active Directory tracks when objects are modified, all I had to do was extract that information and then sort on the last modified date.

The modified date is a good indicator to determine if a security group is in active use. In large organizations, there is a substantial amount of employee turnover or employees being promoted or moved around. If a security group has not been modified in 18 months or more, it’s likely it is no longer necessary. Microsoft has a good article on this:


In order to get that info though, I needed a way to export all of the security groups and pull their modified dates. You can do this through LDAP queries, (per that Microsoft article) but I wanted something a little more robust. I ended up finding this script thatqueries Active Directory to get a list of all user groups, the date it was created and last modified, and then writes the output to a CSV file.

In my case, it returned 7021 security groups. Unfortunately, I also discovered that the script returned no OU’s to tell me where those 7021 groups actually were… which effectively made it pretty useless as I was now dealing with thousands of groups that had not been modified in some time. Knowing which OU they were in, I could at least send a list to the various department heads asking them to audit and verify which groups were still necessary.

So, I modified the script a little (I have pasted the code below) so that it would also return the OU’s by also querying the distinguishedName property. It’s still 7021, but at least now I have it narrowed down just a bit further. : )

Simply copy and paste this code into notepad and save it as GroupAge_AD.vbs. Run it from a command prompt as you would a .bat file and it will create a file called ADGroups_Age.csv in the same directory as the .vbs script.

 ' Name: GroupAge_AD.vbs
 ' Author: Jose Cruz (merlinpr) & modified by Rebecca Harness
 ' Date: 11/04/2009 with modifications on 07/18/2010
 ' Desc: Queries Active Directory for all group objects and writes the date
 ' when it was created and last modified to a CSV file. Modified to add distinguishedName

'On Error Resume Next


Set RootDSE = GetObject("LDAP://RootDSE")
 domainContainer = rootDSE.Get("defaultNamingContext")

Set objConnection = CreateObject("ADODB.Connection")
 Set objCommand = CreateObject("ADODB.Command")
 objConnection.Provider = "ADsDSOObject"
 objConnection.Open "Active Directory Provider"
 Set objCommand.ActiveConnection = objConnection

objCommand.Properties("Page Size") = 1000
 objCommand.Properties("Searchscope") = ADS_SCOPE_SUBTREE

objCommand.CommandText = _
 "SELECT Name, whenChanged, distinguishedName, whenCreated FROM 'LDAP://" & domainContainer & "' WHERE objectCategory='group'"
 Set objRecordSet = objCommand.Execute


Set objFSO = CreateObject("Scripting.FileSystemObject")
 Set objLogFile = objFSO.CreateTextFile("ADGroups_Age.csv", _
 ForWriting, True)

Do Until objRecordSet.EOF
 objLogFile.Write objRecordSet.Fields("Name").Value & ","
 objLogFile.Write objRecordSet.Fields("whenCreated").Value & ","
 objLogFile.Write objRecordSet.Fields("whenChanged").Value & ","
 objLogFile.Write objRecordSet.Fields("distinguishedName").Value