최고의 답변자

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

질문

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.

답변

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

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.

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?)

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.

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

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.

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 ;-)

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?

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.

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.

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

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.

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:

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?

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.