Export the security groups a user belongs to

Posted on 2013-11-27
Last Modified: 2014-01-27

We need to be able to create a report whether that be in excel format or html that will list all which groups (specifically security) as user belongs to. Nested groups also need to be captured. Can anyone help?

I have been using the below so far which does return results but i can't figure out how to filter it to security groups only. Also if i could just pass a text file with names to it instead that would be a bonus too.

#The script will prompt you for a user logon name (no domain)
#If you click cancel or don’t enter a name the script will exit
#The script will continue to prompt you until it gets a valid username.
#The script will then load all the groups the user is a member of and go through all the nested groups reporting the hierarchy and noting any groups the use is a member of more than once.
#At the end it will output a single list sorted by group name to make it easier to find a specific group.
# This is the core function it takes an array of AD groups via distinguished name and a depth field to help with spacing in the output. For each group that is a member of a previous group it will call itself with the new array and depth +1
function GroupEnnumerate ([System.Collections.ArrayList]$InputArray, [int]$Depth)
for ($i=1;$i -le $Depth; $i++) {$spacer=$spacer + "`t"}
#the two above lines add a tab char foe each level deep to keep the output formatted
While ($InputArray.count -gt 0) #repent until the input array is empty
#The next line is important to make sure we have not already processed a group.
#This prevents an endless loop if a group is a member of itself either directly or indirectly
if (!($expandedgrouparray -contains $InputArray[0]))
$GroupName = $InputArray[0]
$expandedgrouparray.add($InputArray[0])|Out-Null #add the new group to the array of processed groups
$Group = [ADSI]("LDAP://"+ $groupname) #lookup the group in AD
$expandedgrouparraynames.add($|Out-Null #add the group friendly name to an array for the output at the end
Write-Host $spacer($Group.Name) #display the group in the output
$TempGroupArray = New-Object System.Collections.ArrayList $null #blank and init the temp array of sub groups
foreach ($x in $group.memberof) # for each sub-group do the following
$member = [ADSI]("LDAP://"+ $x) #lookup the memberof  in AD
If ($member.SchemaClassName -eq "group") #we don't care if the member is not a group, just in case...
$TempGroupArray.add($x)|Out-Null #add the sub-group to a temp array to be used when calling the function for the next loop
if ($TempGroupArray.Count -gt 0) #if we found any sub-groups start checking them
[Array]::Sort([array]$TempGroupArray) # sort the array so he output is easier to read
GroupEnnumerate $TempGroupArray ($Depth+1) # call the function again with the temparray if sub-groups and add another tab to the output
$tempGroupArray=$null #blank out the temp array
$InputArray.remove($InputArray[0]) #as we process each group in the input array we need to remove it
#If we have already processed the group once then just report and remove it from the input array
write-Host -ForegroundColor Red $spacer([ADSI]("LDAP://"+ ($InputArray[0]))).name "- Already a member"
$StartGroupArray = New-Object System.Collections.ArrayList
$ExpandedGroupArray = New-Object System.Collections.ArrayList
$ExpandedGroupArrayNames = New-Object System.Collections.ArrayList
#I create the arrarys as above so I can use the .add .remove etc
$searcher = New-Object System.DirectoryServices.DirectorySearcher([ADSI]'')
$PromptMessage="Please enter the user logon name"
#the following do loop continues until you don't enter anything or you enter a good username
$SAMAccountName = Read-Host $PromptMessage
if ($SAMAccountName) #if there was ANY input in the box
$searcher.Filter = "(&(objectClass=User)(samAccountName=$SAMAccountName))"
$result = $searcher.Findone() # search AD for the requested user
if ($result)
# if we find a user in ID then get the AD object to work with later
# if we don't find a user update the message prompt so the user knows something happened
$PromptMessage= "User " + $samaccountname + " not found - Please enter the user logon name"
exit #if there was not ANY input in the box quit the whole script
while (!$user) # until we have a valid user object in AD keep pestering the operator
$groups = $user.memberof # get a list of all the groups that this user is a member of
foreach($group in $groups)
#for each group that they are a member of add the group DN to the array for processing
$StartGroupArray = $startGroupArray + $group
Write-Host "User: " $user.displayName " " $user.sAMAccountName " is a member of:" #this is the header of the output
[Array]::Sort([array]$startGroupArray)#fort the array so the output is easier to read
GroupEnnumerate $startGroupArray 1 #initiate the top-level group check
Write-Host # space in the output
Write-Host "Complete list sorted by group name" #second output header
$ExpandedGroupArraynames | Sort-Object #sorted list of all the groups processed that the user is a member of.
Write-Host # space in the output
Write-Host "Script Complete"

Open in new window

Question by:stealth82
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 2
LVL 71

Accepted Solution

Chris Dent earned 500 total points
ID: 39683287

Happily it can be simplified a little. LDAP filters are more flexible than you might think.

The filters used here are derived from the documentation here:

The first we use test for the Security flag on the group (the value for that is documented here To do that, we must perform a Binary AND comparison against the groupType attribute. The OID 1.2.840.113556.1.4.803 lets us do that.

The second allows us to have AD tell us about nested group membership, it uses LDAP_MATCHING_RULE_IN_CHAIN which is fantastic for chasing nested things (from this, to manager / directReports and a few others inbetween). That's the 1.2.840.113556.1.4.1941 value.

$Username = "cdent"

# With Quests AD CmdLets:

Get-QADMemberOf $Username -Indirect -LdapFilter "(groupType:1.2.840.113556.1.4.803:=2147483648)"

# Primary groups are excluded from this search. They can be added if required.

# Just good old ADSI
$UserDN = ([ADSISearcher]"(sAMAccountName=$Username)").FindOne().Properties['distinguishedname']

# Query must be executed against a system running no less than 2003 SP1 for this to work.
$Searcher = [ADSISearcher]"(&(member:1.2.840.113556.1.4.1941:=$UserDN)(groupType:1.2.840.113556.1.4.803:=2147483648))"
$Searcher.PageSize = 1000

# Applicable only if you have a Forest to search
$ForestRootDN = ([ADSI]"LDAP://RootDSE").rootDomainNamingContext
$Searcher.SearchRoot = [ADSI]"GC://$ForestRootDN"

# User is a member of these
$Searcher.FindAll() | Select-Object `
  @{n='Name';e={ $_.Properties['name'][0] }},
  @{n='DN';e={ $_.Properties['distinguishedname'] }}

Open in new window

Please let me know if you have any questions about this.


LVL 71

Expert Comment

by:Chris Dent
ID: 39683299
Tweaked slightly to allow reading of usernames from a file, sorting of the return value, and exporting to another file.
# Reading usernames from a file
Get-Content YourListOfUsers.txt | ForEach-Object {
  $Username = $_

  # Primary groups are excluded from this search. They can be added if required.

  # Just good old ADSI
  $UserDN = ([ADSISearcher]"(sAMAccountName=$Username)").FindOne().Properties['distinguishedname']

  # Query must be executed against a system running no less than 2003 SP1 for this to work.
  $Searcher = [ADSISearcher]"(&(member:1.2.840.113556.1.4.1941:=$UserDN)(groupType:1.2.840.113556.1.4.803:=2147483648))"
  $Searcher.PageSize = 1000

  # Applicable only if you have a Forest to search
  # $ForestRootDN = ([ADSI]"LDAP://RootDSE").rootDomainNamingContext
  # $Searcher.SearchRoot = [ADSI]"GC://$ForestRootDN"

  # User is a member of these
  # $Searcher.FindAll() | Select-Object `
  #  @{n='Name';e={ $_.Properties['name'][0] }},
  #  @{n='DN';e={ $_.Properties['distinguishedname'] }}

  # Implementing sorting. Modify the command above to chain Sort-Object
  $Searcher.FindAll() | Select-Object `
      @{n='Username';e={ $Username }},
      @{n='GroupName';e={ $_.Properties['name'][0] }},
      @{n='GroupDN';e={ $_.Properties['distinguishedname'] }} |
    Sort-Object GroupName

# Optional export of everything to another file
} | Export-Csv TheBigGroupReport.csv -NoTypeInformation

Open in new window


Expert Comment

ID: 39686162
Subsun helped me with the Quest tools script below .

Add the SamAccountNames to user.txt

GC C:\user.txt | Get-QADUser | %{
      New-Object PSObject -Property @{
      Firstname = $_.Firstname
      Lastname = $_.Lastname
      DisplayName = $_.DisplayName
      Email = $_.Email
      Title = $_.Title
      group = ""
$_.memberof | %{
      New-Object PSObject -Property @{
      Firstname = ""
      Lastname = ""
      DisplayName = ""
      Email = ""
      Title = ""
      group = ($_ -Split ",")[0] -replace "CN="
} | Select Firstname,Lastname,DisplayName,Email,Title,group | Export-Csv C:\report.csv -NoTypeInformation

Featured Post

Salesforce Made Easy to Use

On-screen guidance at the moment of need enables you & your employees to focus on the core, you can now boost your adoption rates swiftly and simply with one easy tool.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Title # Comments Views Activity
User Account issue 6 44
Exchange Server 2013 Message Tracking Log PowerShell help ? 15 46
BatchFile-Disk Detection 23 35
Change to New Domain, carry Wks configs foward? 4 30
Windows 10 came with  a lot of built in applications, Some organisations leave them there, some will control them using GPO's. This Article is useful for those who do not want to have any applications in their image (example:me).
A company’s centralized system that manages user data, security, and distributed resources is often a focus of criminal attention. Active Directory (AD) is no exception. In truth, it’s even more likely to be targeted due to the number of companies …
The viewer will learn how to create and use a small PHP class to apply a watermark to an image. This video shows the viewer the setup for the PHP watermark as well as important coding language. Continue to Part 2 to learn the core code used in creat…
This Micro Tutorial hows how you can integrate  Mac OSX to a Windows Active Directory Domain. Apple has made it easy to allow users to bind their macs to a windows domain with relative ease. The following video show how to bind OSX Mavericks to …

696 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question