Two VBScript programs, and one PowerShell script, to document all members of a group are linked on this page. The members can be users, contacts, computers, or other groups. The LDAP provider is used to bind to the group. Nested group members are revealed.

The first VBScript program also includes members that have the group, or any nested group, designated as their "Primary Group". The program is designed to be run at a command prompt with the cscript host. The output can be redirected to a text file. The Distinguished Name of the group is passed to the program as a parameter. For example:

cscript //nologo EnumGroup.vbs "cn=Sales,ou=West,dc=MyDomain,dc=com" > Sales.txt

If the Distinguished Name passed to the program has a character that must be escaped, such as a comma, be sure to escape the character with the backslash escape character, "\". The characters that must be escaped are:

, \ / # + < > ; " =

This program should work on any 32-bit Windows client that can log onto the domain. Windows NT and Windows 98/95 clients should have DSClient installed. If DSClient is not installed, they need WSH and ADSI installed. The program has been revised to document the Distinguished Names of the members, instead of the sAMAccountName (pre-Windows 2000 name). This was done because members can be contacts, and Active Directory contact objects do not have a sAMAccountName value. If you are sure no members will be contact objects, you can substitute sAMAccountName for distinguishedName in the script.

EnumGroup.txt <<-- Click here to view or download the program

A PowerShell script that performs the same function is linked below. This should work in PowerShell V1 or V2.

PSEnumGroup.txt <<-- Click here to view or download the program

The programs linked above must bind to all member objects in the group in order to check if the member is a group. If the member is a group, the method that enumerates direct members of the group is called recursively. If the method was called recursively for all members, regardless of class, errors would be raised when any members are not groups.

The second program does not bind to any objects in Active Directory. This should make it faster in many cases. Instead, ADO is used to query Active Directory for all group objects and their member attribute. The member attribute is a collection of the Distinguished Names (DN's) of all direct members of the group. The second program can only output the DN's of all members. Any other attributes would require binding to all member objects, which would slow down the program. The recordset that is retrieved has one row for each group in Active Directory, with the DN of the group and the member collection. The program disconnects the recordset so we can filter the recordset on group DN. The program first filters on the DN of the group specified and a subroutine enumerates all DN's in the member collection (the DN of all direct members of the group). For each member, the subroutine is called recursively, passing the DN of the member. We do not know if the member is a group, but it doesn't matter. When the subroutine filters on a DN that is not a group, the recordset is empty (since the recordset only has groups) and the subroutine exits immediately, so there is no error.

While the recordset returned by this method can be very large, all of the manipulations happen in memory, which is very fast. The speed of this program compared to the one above will depend on the number of groups in Active Directory and the number of members in the group. However, in most cases, it seems to be faster, especially if the group has more than a few members. This program does not reveal any "Primary Group" memberships.

EnumGroup3.txt <<-- Click here to view or download the program

If the group (or any nested groups) has more than 1500 members, then this program must be revised to use ADO range limits. The page "Document Large Group" demonstrates how to do this.