Andr0mega
Andr0mega

Reputation: 65

Get all Groups of a user/group recursively

I have some code to get the groups of a user and write them down into an Arraylist, however ît will only find the groups where a user is directly in. It won't find groups deeper then 1 level.

For example: User is member of Group 1, Group 1 is member of Groups 2, etc. I will only find Group 1. Group 2 won't be written down into my ArrayList.

$Groups = Get-ADPrincipalGroupMembership -Server ESX-DC $GroupName

$GroupArrayList = New-Object System.Collections.ArrayList
foreach ($Group in $Groups)
{
$GroupArrayList.Add($Group.Name) | Out-Null 
} 

Can someone provide me some help here? Thanks.

Upvotes: 0

Views: 1915

Answers (2)

Marcos Oliveira
Marcos Oliveira

Reputation: 509

Recently I found out about tokenGroups attribute, which is way faster than LDAP_MATCHING_RULE_IN_CHAIN, so I'm spreading the word:

To get all AD object groups recursively:

((Get-ADUser username | Get-ADUser -Properties tokenGroups).tokenGroups | Get-ADGroup).Name

Or, if you don't need an ADGroup object, this returns a String instead, but is way faster:

(Get-ADUser username | Get-ADUser -Properties tokenGroups).tokenGroups.Translate([System.Security.Principal.NTAccount]).Value

It's almost instantaneous in our directory:

PS C:\windows\System32> (Get-ADUser -Filter *).Count
86816
PS C:\windows\System32> (Get-ADGroup -filter *).Count
1808
PS C:\windows\System32> (Get-ADComputer -filter *).Count
2666

Just for reference, here how much time it takes in this instance:

# this returns String objects
1..10 | % {
    Measure-Command {
        (Get-ADUser marcos | Get-ADUser -Properties tokenGroups).tokenGroups.Translate([System.Security.Principal.NTAccount]).Value
    }
} | Measure-Object -Average TotalSeconds | select @{l="Type";e={"String"}},Average

# this returns ADGroup objects
1..10 | % {
    Measure-Command {
        ((Get-ADUser marcossantos | Get-ADUser -Properties tokenGroups).tokenGroups | Get-ADGroup).Name
    }
} | Measure-Object -Average TotalSeconds | select @{l="Type";e={"ADGroup"}},Average

Type       Average
----       -------
String  0.01415692
ADGroup 0.25427236

This also returns nested membership of primaryGroup (usually Domain users), which most solutions does not account for.

One downside of this approach is that it does not retrieve distribution groups. If you need that, following query returns just distribution groups, using LDAP_MATCHING_RULE_IN_CHAIN (way faster than retrieving all groups, though):

Get-ADGroup -LDAPFilter "(&(groupType>=0)(member:1.2.840.113556.1.4.1941:=CN=myuser,OU=MyUsers,DC=example,DC=com))"

Upvotes: 0

Patrick
Patrick

Reputation: 2208

I'm not aware of a recurse parameter so I think you have to write that for your own. How ever, I wrote a scripts for similar tasks. Might that helps.

function Get-ADPrincipalGroupMembershipRecurse
{
    param
    (
        [Parameter(Mandatory = $true)]
        [System.String]$Identity   
    )

    $script:Groups = @()

    function Get-NestedAdGroups
    {
        param
        (
            [Parameter(Mandatory = $true)]
            [System.String]$Identity   
        )

        $ADGroup = Get-ADGroup -Identity $Identity -Properties MemberOf, Description
        $script:Groups += $ADGroup

        foreach ($Group in $ADGroup.MemberOf)
        {
            if ($script:Groups.DistinguishedName -notcontains $Group)
            {
                Get-NestedAdGroups -Identity $Group
            }
        }
    }

    foreach ($Group in (Get-ADUser -Identity $Identity -Properties MemberOf).MemberOf)
    {
        Get-NestedAdGroups -Identity $Group
    }

    return ($script:Groups | Sort-Object -Unique)
}

Get-ADPrincipalGroupMembershipRecurse -Identity $SamAccountName

Upvotes: 2

Related Questions