Recursively Search Active Directory Security Groups

Summary: Guest blogger, system admin Marc Carter, talks about recursively searching AD security groups with Windows PowerShell.

Microsoft Scripting Guy, Ed Wilson, is here. Marc Carter is back with us today as our guest blogger. Marc is a system administrator at the Corpus Christi Army Depot. Take it away, Marc…

Like many other mid- to large-sized organizations, we use Active Directory Security Groups to help us manage access and permissions to our organization’s various IT resources (such as file shares and Microsoft SharePoint). By nesting those security groups, we create a parent/child hierarchy that is representative of our organization’s business structure. Throughout most of that business structure, our end-user environment consists of a standard pyramid of three primary groups:

  • Few: executive-level management
  • Several: mid-level chiefs
  • Many: staff members

Employee’s positions within that hierarchy dictate the level of access they have to subordinate resources. The higher you are in the organization’s hierarchy, the more resources you have access to. Members at the staff-level typically only require access to resources that impact their daily tasks and duties, and they aren’t concerned with resources outside their office or work area. Moving up the pyramid, chiefs and executives need access at their level and to the subordinate resources directly under them. We accomplish this one-to-many relationship by adding groups as “Members” to their subordinate security groups.

Thankfully, the Active Directory cmdlets that come with Windows PowerShell 2.0 make it extremely easy to view and manage members of an Active Directory group. By using the Get-ADGroupMember command, you can view the member objects of a group by simply specifying an identity value. In fact any one of the following four options can be used to identify which group you’re interested in:

  • DistinguishedName
  • GUID (objectGUID)
  • Security Identifier (objectSid)
  • Security Accounts Manager (SAM) Account Name (SAMAccountName)

In the following example, I’m using the SAMAccountName of one of the mid-level chief groups to return a list of members.

Image of command output

Text version:

Get-ADGroupMember -Identity “alpha-chiefs”


distinguishedName : CN=roger.moore,OU=Users,OU=M16,DC=contoso,DC=com

name    : roger.moore

objectClass  : user

objectGUID  : 00743c39-22a2-415a-812c-062b4af5c349

SamAccountName : roger.moore

SID    : S-1-3-21-4201780169-38368224-110244791-28519

Using the optional Recursive switch returns a list of accounts that have a membership to the specified group. It provides a bottom-up perspective of membership, and it is effective to see who has permissions on a specific resource.

Get-ADGroupMember -Identity “alpha-staff” -Recursive

Typically, this information is sufficient to analyze and manage resources on a daily basis. However, because of change or transition in our operations, we also have a need to assess our memberships from a top-down view to see not only what resources align under the executive or mid-level managers, but also the staff members who belong to those groups.

To accomplish this, I wrote an advanced function based on the Get-ADGroupMember and Get-ADGroup cmdlets. I use them to traverse the MemberOf attribute and list users within each of the associated subordinate groups.

I begin by defining the VerbosePreference constant and trying to load the Active Directory module (if not already present) within a Try/Catch statement so the script will fail if it’s unable to access the necessary cmdlets:

Image of command output

Text version:

Try {

 If(-not(Get-Module -Name “ActiveDirectory”)){

  Import-Module -Name ActiveDirectory


} Catch {

 Write-Warning “Failed to Import REQUIRED Active Directory Module…exiting script!”

 Write-Warning “`n”



When the script successfully passes this test, I start the process of collecting and storing information within a PSCustomObject array, again using Try/Catch so I don’t end up collecting superfluous or unnecessary information.

On line 59 in the following example, I define the $ADGroup variable with the results of the Get-ADGroup cmdlet and include the Members and MemberOf properties. I’m now ready to start my first loop through the Members collection, adding them to my array. I use an array because I want to collect the name of the Active Directory Group where each member belongs as I traverse down. I use splatting to pass my parameters to my array.


Having recorded all the Members at the current group level, I’m now ready to start my recursion and loop through the MembersOf attribute. This Active Directory property contains all the subordinate groups that the dominate group is a member of (thus the name MemberOf).

Passing this information back to the function, I start the entire process all over again until there are no more subordinate group memberships (that is, MemberOf is empty). I’m now ready to exit my function, but before doing so, I want to display all that information. I simply call the array and then reset the $VerbosePreference constant back to its original state (when I first initiated the script).

I have to point out that our organization has a fairly stringent policy for naming offices and organizations, which makes it easier to sort the data, but this will work for any situation.

Uses for this script

Following are two of my favorite uses for this script:

  • Use Group-Object to provide a quick overview of each group and the count of users (members) within each group.

Image of command output

  • Format output in a table (or use Out-GridView) to get a detailed list by Group of all the members. This is an effective way to provide our administrative staff with a security group view of our organizational roster. It helps us ensure that only the folks who require access to sensitive information have it.

Image of command output

I hope this information helps you better organize your company’s Active Directory groups, memberships, and permissions. You can download this function from the Script Center Repository: Get-MemberOfGroup.

I look forward to your comments and suggestions.


Thank you, Marc.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy