List top users in the most AD groups

active-directorycountgroups

In a Windows domain environment, how could I count the top N users with more group membership than others?

I'd like to know both the indirect and direct groups. So, I'd like this count to include the nested groups too so count the groups that are members of other groups.

I've found another question (Powershell – Find users belonging to more than one AD group) within ServerFault, but I'm afraid that the script from the answer just gives you the direct membership for the users and not the nested groups.

Best Answer

In a Windows domain environment, how could I count the top N users with more groupmembership?

I'd like to know both the indirect and direct groups.

Nested AD Group Membership Counts

The AD enabled user account group membership nested methods can take quite some time so be patient if your AD is large (or not perhaps too). The Powershell logic to get the non-nested AD group membership for all enabled user accounts is below the nested logic I provided if you want to run those. Get-NestedAdGroupMembership Function Source

Top 10 Highest Count Records Output to Console

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


$TempDir    = $env:Temp
$TempPSFile = "$TempDir\~PSTempADMembershipCount.lst"
$Users = Get-ADUser -Filter {Enabled -eq $True} | Select-Object sAMAccountName

If (Test-Path $TempPSFile){
    Remove-Item $TempPSFile
}

ForEach ($User in $Users){
$SamName = $User.sAMAccountName
$Count = Get-NestedAdGroupMembership $User.sAMAccountName | Measure-Object | Select-Object -Expand Count
"$SamName,$count" | Out-File -Filepath $TempPSFile -Append
}

Import-Csv -Header Username,GroupCount -Path $TempPSFile | Sort-Object {[int] $_.GroupCount} -Descending | Select-Object -First 10

All Records Output to Console Descending Order

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


$TempDir    = $env:Temp
$TempPSFile = "$TempDir\~PSTempADMembershipCount.lst"
$Users = Get-ADUser -Filter {Enabled -eq $True} | Select-Object sAMAccountName

If (Test-Path $TempPSFile){
    Remove-Item $TempPSFile
}

ForEach ($User in $Users){
$SamName = $User.sAMAccountName
$Count = Get-NestedAdGroupMembership $User.sAMAccountName | Measure-Object | Select-Object -Expand Count
"$SamName,$count" | Out-File -Filepath $TempPSFile -Append
}

Import-Csv -Header Username,GroupCount -Path $TempPSFile | Sort-Object {[int] $_.GroupCount} -Descending | Select-Object

Non-Nested AD Group Membership Counts

Top 10 Highest Count Records Output to Console

$TempDir    = $env:Temp
$TempPSFile = "$TempDir\~PSTempADMembershipCount.lst"
$Users = Get-ADUser -Filter {Enabled -eq $True} | Select-Object sAMAccountName

If (Test-Path $TempPSFile){
    Remove-Item $TempPSFile
}

ForEach ($User in $Users){
$SamName = $User.sAMAccountName
$Count = Get-ADPrincipalGroupMembership $User.sAMAccountName | Measure-Object | Select-Object -Expand Count
"$SamName,$count" | Out-File -Filepath $TempPSFile -Append
}

Import-Csv -Header Username,GroupCount -Path $TempPSFile | Sort-Object {[int] $_.GroupCount} -Descending | Select-Object -First 10

All Records Output to Console Descending Order

$TempDir    = $env:Temp
$TempPSFile = "$TempDir\~PSTempADMembershipCount.lst"
$Users = Get-ADUser -Filter {Enabled -eq $True} | Select-Object sAMAccountName

If (Test-Path $TempPSFile){
    Remove-Item $TempPSFile
}

ForEach ($User in $Users){
$SamName = $User.sAMAccountName
$Count = Get-ADPrincipalGroupMembership $User.sAMAccountName | Measure-Object | Select-Object -Expand Count
"$SamName,$count" | Out-File -Filepath $TempPSFile -Append
}

Import-Csv -Header Username,GroupCount -Path $TempPSFile | Sort-Object {[int] $_.GroupCount} -Descending

Notes

In all above logic examples, I have the syntax of Get-ADUser -Filter {Enabled -eq $True} in the Get-ADUser command so this filters to ensure you're only querying ENABLED AD user account object. This means DISABLED accounts will NOT be included in the results unless this filter is removed or changed.

The above examples have ALL been tested and confirmed to work as expected in my case, so these have all been verified to work successfully.


Potential Issues

If you run into an issue with an error message of The server was unable to process the request due to an internal error in Powershell when running these processes above, then you may want to take a look at this article for a potential solution that worked in my case (click the provided link).


Further Reading and Resources