Share via


Token Bloat Troubleshooting by Analyzing Group Nesting in AD

This tool started when I was finding ways to analyze the complexity of group memberships in AD. Other than the usual average/median/min/max of number of members, number of memberships etc, I was also interested in finding out the maximum nesting levels of groups and the recursive group membership count. For e.g. in the diagram below, the maximum nesting level of ‘group a’ is 3 and its recursive group membership count is 6.

Image1

Analyzing the recursive group membership of a group is helpful in troubleshooting many scenarios, for e.g. Token Bloat troubleshooting/monitoring, misdirected distribution group memberships etc…

The attached script (Get-ADGroupNesting) retrieves an AD group with 2 additional properties:

1. NestedGroupMembershipCount

2. MaxNestingLevel

Image2

When used with the –ShowTree parameter, the script displays the recursive group membership tree along with emitting the ADGroup object.

Image3

The script can be used with Get-ADPrincipalGroupMembership cmdlet to analyze the recursive group membership of a user/computer/group.

Image4

The above usage can be filtered to analyze the recursive group membership of security groups only, by adding a Where clause … This usage can be utilized in troubleshooting token bloat issues.

Image5

Finally, a sample of usage with Get-ADUser cmdlet:

Image6

Usage:

Step 1 (Important): Map a new AD PowerShell Provider drive to the Global Catalog. And CD to it.

PS C:\> New-PSDrive -PSProvider ActiveDirectory -Server <dc/domain name> -Root "" –GlobalCatalog –Name GC

PS C:\> cd GC:

PS GC:\>

Step2: Use the script Get-ADGroupNesting.ps1 in the below ways.

Here’ the commands in the above screenshots:

1. PS GC:\> Get-ADGroupNesting.ps1 CarAnnounce

2. PS GC:\> Get-ADGroupNesting.ps1 CarAnnounce –ShowTree

3. PS GC:\> Get-ADPrincipalGroupMembership DonFu | % {Get-ADGroupNesting $_} | FT Name,GroupCategory,NestedGroupMembershipCount,MaxNestingLevel –A

4. PS GC:\> Get-ADPrincipalGroupMembership DonFu | Where {$_.GroupCategory -eq "Security"} | % {Get-ADGroupNesting $_ -ShowTree | FT Name,GroupCategory,NestedGroupMembershipCount,MaxNestingLevel -A}

5. PS GC:\> (Get-ADUser DonFu -Properties MemberOf).MemberOf | % {Get-ADGroupNesting.ps1 $_ -ShowTree} | FL DistinguishedName,NestedGroupMembershipCount,MaxNestingLevel

 

cheers,

Dushyant Gill

Program Manager - Microsoft Corporation

 

##########Copy the below script into a new file called Get-ADGroupNesting.ps1

Param (     [Parameter(Mandatory=$true,         Position=0,         ValueFromPipeline=$true,         HelpMessage="DN or ObjectGUID of the AD Group."     )]     [string]$groupIdentity,     [switch]$showTree     )

$global:numberOfRecursiveGroupMemberships = 0 $lastGroupAtALevelFlags = @()

function Get-GroupNesting ([string] $identity, [int] $level, [hashtable] $groupsVisitedBeforeThisOne, [bool] $lastGroupOfTheLevel) {     $group = $null     $group = Get-ADGroup -Identity $identity -Properties "memberOf"        if($lastGroupAtALevelFlags.Count -le $level)     {         $lastGroupAtALevelFlags = $lastGroupAtALevelFlags + 0     }     if($group -ne $null)     {         if($showTree)         {             for($i = 0; $i -lt $level - 1; $i++)             {                 if($lastGroupAtALevelFlags[$i] -ne 0)                 {                     Write-Host -ForegroundColor Yellow -NoNewline "  "                 }                 else                 {                     Write-Host -ForegroundColor Yellow -NoNewline "│ "                 }             }             if($level -ne 0)             {                 if($lastGroupOfTheLevel)                 {                     Write-Host -ForegroundColor Yellow -NoNewline "└─"                 }                 else                 {                     Write-Host -ForegroundColor Yellow -NoNewline "├─"                 }             }             Write-Host -ForegroundColor Yellow $group.Name         }         $groupsVisitedBeforeThisOne.Add($group.distinguishedName,$null)         $global:numberOfRecursiveGroupMemberships ++         $groupMemberShipCount = $group.memberOf.Count         if ($groupMemberShipCount -gt 0)         {             $maxMemberGroupLevel = 0             $count = 0             foreach($groupDN in $group.memberOf)             {                 $count++                 $lastGroupOfThisLevel = $false                 if($count -eq $groupMemberShipCount){$lastGroupOfThisLevel = $true; $lastGroupAtALevelFlags[$level] = 1}                 if(-not $groupsVisitedBeforeThisOne.Contains($groupDN)) #prevent cyclic dependancies                 {                     $memberGroupLevel = Get-GroupNesting -Identity $groupDN -Level $($level+1) -GroupsVisitedBeforeThisOne $groupsVisitedBeforeThisOne -lastGroupOfTheLevel $lastGroupOfThisLevel                     if ($memberGroupLevel -gt $maxMemberGroupLevel){$maxMemberGroupLevel = $memberGroupLevel}                 }             }             $level = $maxMemberGroupLevel         }         else #we've reached the top level group, return it's height         {             return $level         }         return $level     } } $global:numberOfRecursiveGroupMemberships = 0 $groupObj = $null $groupObj = Get-ADGroup -Identity $groupIdentity if($groupObj) {     [int]$maxNestingLevel = Get-GroupNesting -Identity $groupIdentity -Level 0 -GroupsVisitedBeforeThisOne @{} -lastGroupOfTheLevel $false     Add-Member -InputObject $groupObj -MemberType NoteProperty  -Name MaxNestingLevel -Value $maxNestingLevel -Force     Add-Member -InputObject $groupObj -MemberType NoteProperty  -Name NestedGroupMembershipCount -Value $($global:numberOfRecursiveGroupMemberships - 1) -Force     $groupObj }

Comments

  • Anonymous
    February 09, 2010
    Thanks for posting this, group hijacking is a pretty big problem for distribution lists and security groups.  This can happen to a user without them even being aware.
  • Anonymous
    June 07, 2013
    I'm still kind of a powershell n00b.  How do you get the reported info to file, and readable?
  • Anonymous
    January 14, 2015
    I have a lot of groups I want to look at and out-file/ export the results.... For a modification what is the recommended way to import from csv (group names) and out-file, export to a txt file (or another file) that will still maintain the tree (the command from step 2,  #2)?
  • Anonymous
    March 04, 2015
    Unfortunately this does not work for me. Always shows maxNestingLevel 0 and no members.
  • Anonymous
    March 04, 2015
    Hi,I can't access the script once I cd into the GC.Can you help?
  • Anonymous
    April 17, 2015
    fully qualify your filename with complete path