Export the security groups a user belongs to

Hi,

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)
{
$spacer=""
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($group.name.tostring())|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
}
else
{
#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"
$InputArray.remove($InputArray[0])
}
}
}
 
cls
$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]'')
$user=$null
$PromptMessage="Please enter the user logon name"
 
#the following do loop continues until you don't enter anything or you enter a good username
do
{
$SAMAccountName=$null
$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)
{
$user=$result.GetDirectoryEntry()
# if we find a user in ID then get the AD object to work with later
}
else
{
# 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"
}
}
else
{
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

LVL 1
stealth82Asked:
Who is Participating?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

x
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Chris DentPowerShell DeveloperCommented:
Hey,

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:

http://msdn.microsoft.com/en-us/library/windows/desktop/aa746475(v=vs.85).aspx

The first we use test for the Security flag on the group (the value for that is documented here http://msdn.microsoft.com/en-us/library/windows/desktop/ms675935(v=vs.85).aspx). 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.

Cheers,

Chris
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
Chris DentPowerShell DeveloperCommented:
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

Chris
0
MilesLoganCommented:
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
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Powershell

From novice to tech pro — start learning today.