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