Is there a 'Powershell way' to recursively get all groups within a group?

Hi

I'm very new to PS and I've been trying to find a simple 'Powershell way' to get all a user's groups recursively using the MS AD PS module. I have to say I'm extremely surprised there isn't a simple -recurse switch for Get-ADGroup. I'd have thought this was one of the most obvious features to have. Can anyone give me a relatively neat bit of code in as few lines as possible to get all the groups a user belongs to? Of course it hasn't got to include the AD Module.

March 4th, 2012 3:34pm

Get-ADPrincipalGroupMembership -Identity 'bondy666'

See http://technet.microsoft.com/en-us/library/ee617259.aspx for further details.

Cheers

Darrell

Free Windows Admin Tool Kit Click here and download it now
March 4th, 2012 4:14pm

That only returns the top level though, not recurse through those groups.
March 4th, 2012 4:35pm

Consider the input you are giving:  a user name.  Given than, what further can be done, that getting all the groups that user belongs to?  What will be returned will be a bunch of groups, of which we know the given user is a member of.

So what's the next question?  Of those groups, ...(you want to know what groups they are members of, and so on?)

Now consider the output.  How do you envisage the report to look?

Free Windows Admin Tool Kit Click here and download it now
March 4th, 2012 4:41pm

Specifically, the top level groups are no use to me. These remain untouched and it is the nested groups which certain admins have access to update and create. I don't necessarily know what level these will be on because of the convoluted way the company I am currently working for set them up so to be clear:

               PARENT GROUP (User is direct member)

                                         |

                              CHILD GROUP

                                        |

                            FURTHER CHILD, etc

I need to be able to see the 'FURTHER CHILD' in the eg above as well as the top level.

Thanks

March 4th, 2012 5:13pm

Eureka, I think I have it!

function getAdGroupMembership ($user) {
   $groups = @(Get-ADPrincipalGroupMembership -Identity $user | select -ExpandProperty distinguishedname)
   $groups
   if ($groups.count -gt 0) {
        foreach ($group in $groups) {
            getAdGroupMembership $group
            }
        }
    }
    
getAdGroupMembership 'CN=Sam Willow,OU=UserAccounts,DC=contoso,DC=local'		
Free Windows Admin Tool Kit Click here and download it now
March 4th, 2012 5:25pm

Getting closer but not quite there. Seems a few groups are missing for some reason.

Must confess my attempts so far have also resulted in infinite loops too (not that this one does).

March 4th, 2012 5:29pm

Have you tried it with the revised "Eureka" code?

Free Windows Admin Tool Kit Click here and download it now
March 4th, 2012 5:33pm

Yes, and there's the weird thing: it IS returning more groups (specifically built-in domain groups I'd not thought about) but it's not returning all the nested groups I've created. I'm currently trying to figure out where it's going wrong, I'm sure we-re in spitting distance of the resolution...
March 4th, 2012 5:37pm

Eureka, I think I have it!

function getAdGroupMembership ($user) {
   $groups = @(Get-ADPrincipalGroupMembership -Identity $user | select -ExpandProperty distinguishedname)
   $groups
   if ($groups.count -gt 0) {
        foreach ($group in $groups) {
            getAdGroupMembership $group
            }
        }
    }
    
getAdGroupMembership 'CN=Sam Willow,OU=UserAccounts,DC=contoso,DC=local'		
Free Windows Admin Tool Kit Click here and download it now
March 4th, 2012 8:50pm

Hi Al.  Two comments:

1.  If you have a circular loop membership, it's bad.  Maybe it's a good thing if it goes into an infinite loop in this case?  I don't know, but I don't think circular group memberships are a good or useful or sensible thing.  Richard Mueller has even written a  script specifically to track these down.  (can't find the link now, but will if you want).

2. Good point about the if test.  As you say, the foreach will not execute anyway if $group.count -eq 0. 

March 4th, 2012 9:07pm

Circular nested groups can wreck havoc. My script to search for them (so you can fix the situation) linked here:

http://gallery.technet.microsoft.com/fa4ccf4f-712e-459c-88b4-aacdb03a08d0

Free Windows Admin Tool Kit Click here and download it now
March 4th, 2012 10:24pm

If you have circular group membership, it would be better to run a script that could specifically identify that situation than to try to deduce it when a script used for a different purpose experiences an infinite loop. More so when the script is being run as a logon script by a user who is likely not in a position to appreciate the problem ;-)
March 5th, 2012 12:13am

What kind of havoc does circular group nesting wreak?

The only thing I can see that it obviously does do is cause infinite loops in scripts that implicitly assume groups are not nested inappropriately.

It does not appear to interfere with the intended NTFS permissions derived from group membership, and the code that builds the session security token at logon does not suffer an infinite loop.

It does not appear to cause users in loopily nested distribution lists to get an infinite number of copies of messages send to either list.

One positive, but perhaps not earth-shattering thing it does do is...

Suppose you had two distribution lists representing, say: "first floor IT staff" and "second floor IT staff". Then let us further suppose that your company moved to another location where they occupied only the first floor. You could move the members of the second floor group into the first floor group and delete the second. Then you would have to tell everyone to use on the the first floor group. And deal with support calls when they continued using the second floor group, perhaps by replying to an old message.

The other solution is to make each list a member of the other. Then your users could continue using whichever list they were familiar with.

So my question to Richard is: what actual problems does circular nesting cause?

Free Windows Admin Tool Kit Click here and download it now
March 5th, 2012 12:25am

The only problem I know of is the possibility of an infinite loop. The logon token is not affected, nor permissions. The AD modules take the possibility of circular nesting into account and avoid the problem. Some third party apps that expand group membership do not and can get caught in an infinite loop. However, I would argue that circular nesting also makes no sense, and should be corrected. I'm not sure about your first floor, second floor example.

March 5th, 2012 3:38am

The only problem I know of is the possibility of an infinite loop. The logon token is not affected, nor permissions. The AD modules take the possibility of circular nesting into account and avoid the problem. Some third party apps that expand group membership do not and can get caught in an infinite loop. However, I would argue that circular nesting also makes no sense, and should be corrected. I'm not sure about your first floor, second floor example.

Free Windows Admin Tool Kit Click here and download it now
March 5th, 2012 9:00am

Firstly I'd like to thank you all for your contributions to what has turned out to be a topic far less straightforward than I originally thought (and indeed think it should be!). It looks as though some of the AD groups at work are indeed circularly nested and I will borrow some of the scripts mentioned to find more so thanks for those.

I believe I have got as close to the solution as I am going to get now with the code below

$ARR = ([System.Security.Principal.WindowsIdentity]::GetCurrent().Groups.Translate([System.Security.Principal.NTAccount]))

Foreach ($grp in $ARR) {

     if ($grp -like '*-MAP*') { < DO SOMETHING >}

}

This is reasonably brief and although it still doesn't fully list all nested groups (and I still can't fathom why this is), it does get a similar list to something like gpresult, whoami, etc (which incidentally, also don't display all nested groups in the way I showed earlier in the thread either). If any Microsoft bods reading this can explain why this behaviour occurs I'd love to know because I have tested the above (as well as gpresult and whoami) on very basic users accounts with few groups and no circular nesting within them yet still not all (nested) groups showing.

I would hope that any future updates to powershell include a recursive switch that was able to accurately traverse all a user's groups while looking out for circular nesting.

  • Marked as answer by bondy666 Tuesday, March 06, 2012 2:05 PM
March 6th, 2012 2:04pm

Richard has written quite a few script to check for group membership recursively.  They are described here:

http://www.rlmueller.net/freecode1.htm

Free Windows Admin Tool Kit Click here and download it now
March 6th, 2012 2:06pm

Firstly I'd like to thank you all for your contributions to what has turned out to be a topic far less straightforward than I originally thought (and indeed think it should be!). It looks as though some of the AD groups at work are indeed circularly nested and I will borrow some of the scripts mentioned to find more so thanks for those.

I believe I have got as close to the solution as I am going to get now with the code below

$ARR = ([System.Security.Principal.WindowsIdentity]::GetCurrent().Groups.Translate([System.Security.Principal.NTAccount]))

Foreach ($grp in $ARR) {

     if ($grp -like '*-MAP*') { < DO SOMETHING >}

}

This is reasonably brief and although it still doesn't fully list all nested groups (and I still can't fathom why this is), it does get a similar list to something like gpresult, whoami, etc (which incidentally, also don't display all nested groups in the way I showed earlier in the thread either). If any Microsoft bods reading this can explain why this behaviour occurs I'd love to know because I have tested the above (as well as gpresult and whoami) on very basic users accounts with few groups and no circular nesting within them yet still not all (nested) groups showing.

I would hope that any future updates to powershell include a recursive switch that was able to accurately traverse all a user's groups while looking out for circular nesting.

  • Marked as answer by bondy666 Tuesday, March 06, 2012 2:05 PM
March 6th, 2012 5:04pm

If you are only concerned with security groups (and one domain), the most efficient method is to use the tokenGroups attribute of the user. I have a PowerShell function demonstrating how to use tokenGroups linked here:

http://www.rlmueller.net/IsMember4.htm

The attribute is operational and is a collection of objectSID values, so using it is a bit complex, but the collection resolves all nesting, and even includes the "primary" group.

The PowerShell script is also linked here in the script gallery:

http://gallery.technet.microsoft.com/5adf9ad0-1abf-4557-85cd-657da1cc7df4

Free Windows Admin Tool Kit Click here and download it now
March 6th, 2012 8:39pm

Brilliant! If I understand the first script correctly, it accesses the security token of the active session. I'm not sure if it is getting the token from the user's computer or from active directory, though. It would be faster if it comes from the computer, as there would be no communication with the DC, other than perhaps to translate the SID to the account name, although that also appears to work with what is on the machine.

As I might have mentioned earlier or elsewhere, we use IFMEMBER.EXE which does the same thing. Unfortunately it chokes on accounts having a large number of group memberships because of a fixed size buffer. If that is where your script gets the info from, I would hope and expect that it does not suffer from the same limitation. Do you know?

March 6th, 2012 9:16pm

The tokenGroups is an attribute of the AD user object, so it is retrieved from AD. It is a collection of group objectSID values, so each SID must be translated into a group name, which also involves AD (although the translation is efficient). It corresponds to the token the user gets when they logon. It saves recursive group expansion, plus includes the "primary" group without extra effort. I'm not aware of any limitations.

Free Windows Admin Tool Kit Click here and download it now
March 6th, 2012 11:46pm

Hi bondy666,

this is how we do it using .NET assemblies in PowerShell (our .NET geek guided me to and through this).

Just insert your $name (SamAccountName):

$name = "NAME"
$assembly = [System.Reflection.Assembly]::LoadWithPartialName("System.DirectoryServices.AccountManagement")
$context = New-Object -typename "System.DirectoryServices.AccountManagement.PrincipalContext" -argumentlist $([System.DirectoryServices.AccountManagement.ContextType]::Domain)
$user = [System.DirectoryServices.AccountManagement.UserPrincipal]::FindByIdentity($context,$([System.DirectoryServices.AccountManagement.IdentityType]::SamAccountName),$name)
$user.GetAuthorizationGroups() | %{Write-Host $_.SamAccountName}

Hope that helps!

Andy

March 22nd, 2012 8:02pm

FWIW:

Get-ADUser -Filter {(memberof -recursivematch 'cn=test,ou=useraccounts,dc=contoso,dc=local')}

Free Windows Admin Tool Kit Click here and download it now
March 22nd, 2012 8:14pm

Try this one! It`s work perfect!

$domain = $env:userdomain+"\"
    $out = @()
    $ht=@{}
    $i = 0
    while ($i -lt $ht.Count)
    {
        $str = "LDAP://" + $ht[$i]
        $adobj = ([adsi]$str)
        $mmbrOf = $adobj.memberof
        foreach ($row in $mmbrOf)
        {
            if (-not $ht.ContainsValue($row)){$ht.Add($ht.Count, $row)}
        }
        $out += $domain + [string]$adobj.samaccountname
        $i ++
        $adobj = $null
    }
    $users =  $out | sort

  • Edited by Alvalvar Thursday, August 21, 2014 6:00 AM
August 21st, 2014 5:53am

Try this one! It`s work perfect!

$domain = $env:userdomain+"\"
    $out = @()
    $ht=@{}
    $i = 0
    while ($i -lt $ht.Count)
    {
        $str = "LDAP://" + $ht[$i]
        $adobj = ([adsi]$str)
        $mmbrOf = $adobj.memberof
        foreach ($row in $mmbrOf)
        {
            if (-not $ht.ContainsValue($row)){$ht.Add($ht.Count, $row)}
        }
        $out += $domain + [string]$adobj.samaccountname
        $i ++
        $adobj = $null
    }
    $users =  $out | sort

  • Edited by Alvalvar Thursday, August 21, 2014 6:00 AM
Free Windows Admin Tool Kit Click here and download it now
August 21st, 2014 8:53am

I used a variation on this.  I just used the distinguished name for the hash instead of the samaccount, and I modified it slightly so you don't have to initialize the empty hash outside the function:

function Get-NestedAdGroupMembership {

Param(
	[parameter(Mandatory=$true)]
	[alias("account", "username")]
        $user,
	[parameter(Mandatory=$false)]
	$grouphash = @{}
	)

   $groups = @(Get-ADPrincipalGroupMembership -Identity $user | select -ExpandProperty distinguishedname)
   foreach ($group in $groups) {
      if ( $grouphash[$group] -eq $null) {
         $grouphash[$group] = $true
         $group
         Get-NestedAdGroupMembership $group $grouphash
      }
   }
}

And then you can just call it with:

Get-NestedAdGroupMembership testaccount

February 20th, 2015 9:47pm

You might want to split value of $group by "," to receive only cn or samaccountname attribute of the group.

[string]$gr = $group.split(",")
$group_cn = $gr[0]
$group

Free Windows Admin Tool Kit Click here and download it now
July 9th, 2015 10:16am

This topic is archived. No further replies will be accepted.

Other recent topics Other recent topics