PowerShell: Get *All* Domain User’s Group Memberships
Hi All – Need to get all of a user’s group memberships – both domain and local? Do you need indirect memberships also? Then read on!
I’m busy installing Lync Server 2013 and came to a point where I needed to add my “Lync Admin” user to a local user group on the Lync dedicated front-end.
But – I was pretty sure that my “Lync Admin” user already had this membership, via some indirect group. However, I was too lazy to want to track this down; I want the system to tell me *all* of my user’s group memberships. This includes both domain and local memberships. Sounds simple, but it was more complex than I thought.
I turned to PowerShell for the answer. Here’s what I do:
- Get the domain group memberships for my user. This is easy enough;
$adGroups = (Get-AdUser $userName -Properties MemberOf).MemberOf
- Found and modified a recursive function that would go check indirect group memberships, passing in the groups I got from above as a starter set.
- Then things got tricky; there’s no “recursive” local group membership that I could find. So I resorted to an ugly set of nested loops to check for all group memberships.
It’s a simple script although not particularly fast. Hope it’s useful to all!
# list-user-groups.ps1, ABr, 20141231 # list all group memberships - domain and local - for the passed account. # assumes current domain (just use the account name, no domain). # example use: list-user-groups.ps myAccount param( [string]$userName ) # we require AD module import-module activedirectory # this recursive function takes in a list of groups and recursively # gets all indirect group memberships from the current domain. Function Get-ADGroupsRecursive{ Param([String[]]$Groups) Begin{ $Results = @() } Process{ ForEach($Group in $Groups){ $Results+=$Group ForEach($Object in (Get-ADGroupMember $Group|?{$_.objectClass -eq "Group"})){ $Results += Get-ADGroupsRecursive $Object } } } End{ $Results | Select -Unique } } # we'll assume we're running on the current system $Computer = [ADSI]"WinNT://$env:COMPUTERNAME" # all groups is the result (starts empty) $allGroups = @() # get the Active Directory user object, including group memberships $user = Get-AdUser $userName -Properties MemberOf # now expand the current list $userAdGroups = $user.MemberOf $userAdGroups += $userAdGroups | %{Get-ADGroupsRecursive $_} # remove any duplicates $userAdGroups = $userAdGroups | Select -Unique # the AD groups come back with FQDN. this is nice, but the PsBase calls # below just return the group name. to make it easy for me, I'm extracting # the group name here from the FQDN. ForEach ($userAdGroup in $userAdGroups) { $found = $userAdGroup -Match 'CN=([^,]+).*' If ($found) { $allGroups += $matches[1] } } # now get all local groups $localGroups = $Computer.psbase.Children | Where {$_.psbase.schemaClassName -eq "group"} # this gets ugly. we need to iterate over all local groups repeatedly. $foundGroup = $TRUE $iterCount = 0 while ($foundGroup) { # keep track of how many times we've done this foolishness... $iterCount += 1 Write-Debug "Iteration $iterCount..." # reset our flag; we'll assume we don't find any more indirect memberships $foundGroup = $FALSE ForEach ($localGroup in $localGroups) { # get the name of this local group $localGroupName = $localGroup.GetType().InvokeMember("Name", 'GetProperty', $Null, $localGroup, $Null) # quick check to see if the local group is already in our result list $foundLocalGroup = $allGroups -Contains $localGroupName if ($foundLocalGroup) { Continue } # it's not...we need to check this local group by looking at its members Write-Debug " Checking group '$localGroupName'..." $localGroupMembers = @($localGroup.psbase.Invoke("Members")) ForEach ($localGroupMember In $localGroupMembers) { # extract local group member info $localGroupMemberClass = $localGroupMember.GetType().InvokeMember("Class", 'GetProperty', $Null, $localGroupMember, $Null) $localGroupMemberName = $localGroupMember.GetType().InvokeMember("Name", 'GetProperty', $Null, $localGroupMember, $Null) # should this local group be added? this will be the case if the local # group member is one of our AD groups *or* if the user account is in # the local group $addLocalGroup = $False $localGroupMemberIsGroup = ([string]::Compare($localGroupMemberClass, "Group", $True) -eq 0) if (-Not $localGroupMemberIsGroup) { # this local group member is a user...is it us?? if ([string]::Compare($localGroupMemberName, $userName, $True) -eq 0) { # ah, our AD account is in this local group. let's add the local group # to our list of groups. $addLocalGroup = $True } } else { Write-Debug " Checking subgroup '$localGroupMemberName'..." # the logic here is: is this sub-group in our list of discovered groups? $foundLocalGroupMember = $allGroups -Contains $localGroupMemberName if ($foundLocalGroupMember) { # woohoo. we found an indirect membership. $addLocalGroup = $True } } if ($addLocalGroup) { # add the local group to our list of all groups Write-Debug " Added local group '$localGroupMemberName'..." $allGroups += $localGroupName # set our flag to continue the controlling iteration... $foundGroup = $True # ...but we don't need to check this local group anymore. Break } } } # so why the outer loop? consider this group membership chain: # domainUser = my account # domainGroup = a domain group with my account # on the local computer, we have two groups: # localGroupA - contains localGroupB as a member # localGroupB - contains domainGroup as a member # here's the logic: # 1. My list of discovered groups starts only with "domainGroup". # 2. I check localGroupA - it has only localGroupB as a member, which fails my test. # So, I ignore localGroupA. # 3. I check localGroupB - aha! it has domainGroup as a member, and I'm a member # a member of domainGroup, so I add localGroupB to my list. # the problem? because I check localGroupA *before* I get to localGroupB, I never # discover that I have an indirect membership (via my domain group membership in # localGroupB). the outer WHILE loop, in conjunction with my "$foundGroup" flag, # solves this problem. } # print the list of all groups...sorry, no indication of whether they are # domain or local. maybe next script ;) $allGroups
Leave a Reply