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

    Question

  • 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.

    dimanche 4 mars 2012 12:34

Réponses

  • 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.

    • Marqué comme réponse bondy666 mardi 6 mars 2012 14:05
    mardi 6 mars 2012 14:04

Toutes les réponses

  • Get-ADPrincipalGroupMembership -Identity 'bondy666'

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

    Cheers

    Darrell

    dimanche 4 mars 2012 13:14
  • That only returns the top level though, not recurse through those groups.
    dimanche 4 mars 2012 13:35
  • 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? 


    Grant Ward, a.k.a. Bigteddy

    What's new in Powershell 3.0 (Technet Wiki)


    • Modifié Bigteddy dimanche 4 mars 2012 13:49
    dimanche 4 mars 2012 13:41
  • 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

    dimanche 4 mars 2012 14:13
  • 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'
        


    Grant Ward, a.k.a. Bigteddy

    What's new in Powershell 3.0 (Technet Wiki)


    • Modifié Bigteddy dimanche 4 mars 2012 14:28
    dimanche 4 mars 2012 14:25
  • 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).

    dimanche 4 mars 2012 14:29
  • Have you tried it with the revised "Eureka" code?


    Grant Ward, a.k.a. Bigteddy

    What's new in Powershell 3.0 (Technet Wiki)

    dimanche 4 mars 2012 14:33
  • 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...
    dimanche 4 mars 2012 14:37
  • 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'
        


    Grant Ward, a.k.a. Bigteddy

    What's new in Powershell 3.0 (Technet Wiki)



    I believe that will not avoid an infinite loop in the case where group A is a member of group B and group B is a member of group A.

    I would use a hashtable to keep from recursing into any group more than once. Here is my (untested) attempt:

    function getAdGroupMembership ($user) {
       $groups = @(Get-ADPrincipalGroupMembership -Identity $user | select -ExpandProperty distinguishedname)
       foreach ($group in $groups) {
          $groupsam = $group.samaccountname  # ???
          if ( $grouphash[$groupsam] -eq $null]) {
             $grouphash[$groupsam] = $true
             $group
             getAdGroupMembership $group
          }
       }
    }
    $grouphash = @{}    
    getAdGroupMembership 'CN=Sam Willow,OU=UserAccounts,DC=contoso,DC=local'

    Note that I do not have the AD cmdlets, so am not sure if the statement with the "???" comment will actually return the samaccountname of the group.

    Note also that the "if ($groups.count -gt 0)" test is not required. If foreach encounters no objects, it will do precisely nothing.


    Al Dunbar

    dimanche 4 mars 2012 17:50
  • 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. 


    Grant Ward, a.k.a. Bigteddy

    What's new in Powershell 3.0 (Technet Wiki)

    dimanche 4 mars 2012 18:07
  • 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


    Richard Mueller - MVP Directory Services

    dimanche 4 mars 2012 19:24
  • 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 ;-)

    Al Dunbar

    dimanche 4 mars 2012 21:13
  • 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?


    Al Dunbar

    dimanche 4 mars 2012 21:25
  • 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.


    Richard Mueller - MVP Directory Services

    lundi 5 mars 2012 00:38
  • 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.


    Richard Mueller - MVP Directory Services

    I agree that circular nesting is generally not a good idea, if only because it is illogical and should generally be unnecessary. But the fact remains that, unlike the creation of accounts with duplicate samaccountnames, there is nothing preventing it. For this reason, any scripting that recurses group memberships should accommodate it, or perhaps report it. You could perhaps devise an operational policy against it, but if it is not routinely tested for, it could creep back into the picture.

    The example I gave is trivial, however I used the technique once when I found that two security groups seemed to represent the same collection of users (though the membership was different), and used to permit who knows how many folders. It was a temporary arrangement of course, and we eventually fixed it when we found out which one we could delete after experimentally finding all of the folders permitted to it.


    Al Dunbar

    lundi 5 mars 2012 06:00
  • Richard has written quite a few script to check for group membership recursively.  They are described here:

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


    Grant Ward, a.k.a. Bigteddy

    What's new in Powershell 3.0 (Technet Wiki)

    mardi 6 mars 2012 11:06
  • 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.

    • Marqué comme réponse bondy666 mardi 6 mars 2012 14:05
    mardi 6 mars 2012 14:04
  • 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


    Richard Mueller - MVP Directory Services


    mardi 6 mars 2012 17:39
  • 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?


    Al Dunbar

    mardi 6 mars 2012 18:16
  • 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.


    Richard Mueller - MVP Directory Services

    mardi 6 mars 2012 20:46
  • 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

    jeudi 22 mars 2012 17:02
  • FWIW:

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


    Grant Ward, a.k.a. Bigteddy

    What's new in Powershell 3.0 (Technet Wiki)

    jeudi 22 mars 2012 17:14