4

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.

Hennes
  • 4,772
  • 1
  • 18
  • 29
fedayn
  • 95
  • 1
  • 7
  • Use the references script, but get the groups.count for each user and store it in an array with username. I dare say you should probably be more concerned with WHICH groups than just sheer number of groups a principal has joined. – blaughw Jan 14 '16 at 19:11

3 Answers3

3

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

Pimp Juice IT
  • 1,010
  • 1
  • 9
  • 16
  • I got this error output: _Test-Path : Cannot bind argument to parameter 'Path' because it is null. At N:\Support\Microsoft\Powershell\ad\Users\AllGroupFor_Top10_User.ps1:28 char: 14 + If (Test-Path <<<< $TempPSScript1){ + CategoryInfo : InvalidData: (:) [Test-Path], ParameterBindingVa lidationException + FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,M icrosoft.PowerShell.Commands.TestPathCommand_ – fedayn Jan 25 '16 at 10:53
  • I finally used your first script and worked like a charm. As you pointed out correctly, it takes a lot of time to show the output, so it's a good idea to set a few filters in order to spped up the script execution (I ran the script with the _searchbase_ parameter). – fedayn Jan 27 '16 at 08:15
0

To display the list of groups with nested groups

dsget user "cn=Jon Smith,cn=users,dc=microsoft,dc=com" -memberof -expand

dsquery user -name "Jon Smith" | dsget user -memberof -expand

this is for a single user, for all the use you may need a script

0

My environment is slated for upgrade so I can't test this, but something like

Get-ADUser -Filter * -Properties memberOf | Where-Object {$_.memberof.count -ge 1} | Sort-Object -Property $_.memberof.count -Descending | Select-Object -First 10

might do the trick.

Katherine Villyard
  • 18,510
  • 4
  • 36
  • 59