find member,memberOf for Groups only

Tagged: 

This topic contains 8 replies, has 4 voices, and was last updated by  Curtis Smith 1 year, 2 months ago.

  • Author
    Posts
  • #46368

    Jeff Taylor
    Participant

    I'm trying to pull a report that:

    only lists Security Groups that have other Security Groups as members and/or are memberOf other security groups.

    I have this code so far:

    Get-ADGroup -Filter {GroupCategory -eq 'security'} -SearchBase 'DC=child,DC=root,DC=com' -Credential $creds -Properties * | select member,memberOf

    I want to then pull the DN, SamAccountName for those results but haven't gotten close to narrowing down the initial results.

    thanks

  • #46479

    Anthony Stringer
    Participant

    not fully tested, but you might try something like this

    $notsecuritygroup = New-Object System.Collections.ArrayList
    
    Write-Host 'getting all security groups...'
    $groups = Get-ADGroup -Filter "groupcategory -eq 'security'" -properties member | select name, member
    
    # all groups progress
    $index = 0
    $total = $groups.Count
    $starttime = $lasttime = Get-Date
    
    # all members progress
    $index2 = 0
    $total2 = $groups.member.Count
    $starttime2 = $lasttime2 = Get-Date
    
    $results = foreach ($group in $groups) {
        # all groups progress
        $index++
        $currtime = (Get-Date) - $starttime
        $avg = $currtime.TotalSeconds / $index
        $last = ((Get-Date) - $lasttime).TotalSeconds
        $left = $total - $index
        $WrPrgParam = @{
            Activity = (
                "ALL GROUPS $(Get-Date -f s)",
                "Total: $($currtime -replace '\..*')",
                "Avg: $('{0:N2}' -f $avg)",
                "Last: $('{0:N2}' -f $last)",
                "ETA: $('{0:N2}' -f ($avg * $left / 60))",
                "min ($([string](Get-Date).AddSeconds($avg*$left) -replace '^.* '))"
            ) -join ' '
            Status = "$index of $total ($left left) [$('{0:N2}' -f ($index / $total * 100))%]"
            CurrentOperation = "GROUP: $($group.name)"
            PercentComplete = $index / $total * 100
        }
        Write-Progress @WrPrgParam
        $lasttime = Get-Date
        
        # all members progress
        $index3 = 0
        $total3 = $group.member.Count
        $starttime3 = $lasttime3 = Get-Date
        foreach ($member in $group.member) {
            $index2++
            $currtime2 = (Get-Date) - $starttime2
            $avg2 = $currtime2.TotalSeconds / $index2
            $last2 = ((Get-Date) - $lasttime2).TotalSeconds
            $left2 = $total2 - $index2
            $WrPrgParam2 = @{
                Activity = (
                    "ALL MEMBERS $(Get-Date -f s)",
                    "Total: $($currtime2 -replace '\..*')",
                    "Avg: $('{0:N2}' -f $avg2)",
                    "Last: $('{0:N2}' -f $last2)",
                    "ETA: $('{0:N2}' -f ($avg2 * $left2 / 60))",
                    "min ($([string](Get-Date).AddSeconds($avg2*$left2) -replace '^.* '))"
                ) -join ' '
                Status = "$index2 of $total2 ($left2 left) [$('{0:N2}' -f ($index2 / $total2 * 100))%]"
                CurrentOperation = "MEMBER: $member"
                PercentComplete = $index2 / $total2 * 100
                id = 2
            }
            Write-Progress @WrPrgParam2
            $lasttime2 = Get-Date
    
            # current members progress
            $index3++
            $currtime3 = (Get-Date) - $starttime3
            $avg3 = $currtime3.TotalSeconds / $index3
            $last3 = ((Get-Date) - $lasttime3).TotalSeconds
            $left3 = $total3 - $index3
            $WrPrgParam3 = @{
                Activity = (
                    "CURRENT GROUP MEMBERS $(Get-Date -f s)",
                    "Total: $($currtime3 -replace '\..*')",
                    "Avg: $('{0:N2}' -f $avg3)",
                    "Last: $('{0:N2}' -f $last3)",
                    "ETA: $('{0:N2}' -f ($avg3 * $left3 / 60))",
                    "min ($([string](Get-Date).AddSeconds($avg3*$left3) -replace '^.* '))"
                ) -join ' '
                Status = "$index3 of $total3 ($left3 left) [$('{0:N2}' -f ($index3 / $total3 * 100))%]"
                CurrentOperation = "MEMBER: $member"
                PercentComplete = $index3 / $total3 * 100
                id = 3
            }
            Write-Progress @WrPrgParam3
            $lasttime3 = Get-Date
    
            $group | select name, @{n='member';e={
                $_.member | ? {
                    $_ -notin $notsecuritygroup -and
                    $(try {
                        (get-adobject $_ -prop groupcategory).groupcategory -eq 'security'
                    } catch {
                        $null = $notsecuritygroup.Add($_)
                    })
                }
            }} | ? member
        }
    }
    
    $results
    
  • #46491

    Craig Duff
    Participant
    Get-ADGroup -Filter {GroupCategory -eq 'security'} -Properties memberof,members |
    Where-Object {
    
            $members = $_.members |
            Get-ADObject |
            Where-Object {
                $_.ObjectClass -eq 'group'
            } | 
            Get-ADGroup |
            Where-Object {
                $_.GroupCategory -eq 'security'
            }
    
            $memberof = $_.memberof |
            Get-ADObject |
            Where-Object {
                $_.ObjectClass -eq 'group'
            } | 
            Get-ADGroup |
            Where-Object {
                $_.GroupCategory -eq 'security'
            }
    
            $members -or $memberof
    
    } |
    Select-Object -Property DistinguishedName,SamAccountName

    This isn't very efficient, as it results in a lot of calls to AD, but I think it'll get the list you want.

    • #47403

      Jeff Taylor
      Participant

      ~

      • This reply was modified 1 year, 2 months ago by  Jeff Taylor.
    • #47406

      Jeff Taylor
      Participant

      Thanks Craig, I'm afraid this one took way too long to get results. We have about 62k User objects in our forest

  • #46528

    Curtis Smith
    Participant

    Here is an example to query AD once and then work with the returned data

    $groups = Get-ADGroup -Filter {GroupCategory -eq 'security'} -Properties member,memberof
    
    $groups.member | ForEach-Object {
        If ($groups.distinguishedname -contains $_) {
            $current=$_
            $groups | Where-Object {$_.member -contains $current} | Select-Object distinguishedname, samaccountname, @{Label='Type';Expression={"HasMember"}}, @{Label='RelativeGroup';Expression={$current}}
        }
    } | Select-Object * -Unique
    
    $groups.memberof | ForEach-Object {
        If ($groups.distinguishedname -contains $_) {
            $current=$_
            $groups | Where-Object {$_.memberof -contains $current} | Select-Object distinguishedname, samaccountname, @{Label='Type';Expression={"IsMemberOf"}}, @{Label='RelativeGroup';Expression={$current}}
        }
    } | Select-Object * -Unique
    

    Results:

    distinguishedname                                                     samaccountname                         Type       RelativeGroup                                                  
    -----------------                                                     --------------                         ----       -------------                                                  
    CN=Denied RODC Password Replication Group,CN=Users,DC=domain,DC=local Denied RODC Password Replication Group HasMember  CN=Read-only Domain Controllers,CN=Users,DC=domain,DC=local          
    CN=Denied RODC Password Replication Group,CN=Users,DC=domain,DC=local Denied RODC Password Replication Group HasMember  CN=Group Policy Creator Owners,CN=Users,DC=domain,DC=local           
    CN=Denied RODC Password Replication Group,CN=Users,DC=domain,DC=local Denied RODC Password Replication Group HasMember  CN=Domain Admins,CN=Users,DC=domain,DC=local                         
    CN=Administrators,CN=Builtin,DC=domain,DC=local                       Administrators                         HasMember  CN=Domain Admins,CN=Users,DC=domain,DC=local                         
    CN=Denied RODC Password Replication Group,CN=Users,DC=domain,DC=local Denied RODC Password Replication Group HasMember  CN=Cert Publishers,CN=Users,DC=domain,DC=local                       
    CN=Denied RODC Password Replication Group,CN=Users,DC=domain,DC=local Denied RODC Password Replication Group HasMember  CN=Enterprise Admins,CN=Users,DC=domain,DC=local                     
    CN=Administrators,CN=Builtin,DC=domain,DC=local                       Administrators                         HasMember  CN=Enterprise Admins,CN=Users,DC=domain,DC=local                     
    CN=Denied RODC Password Replication Group,CN=Users,DC=domain,DC=local Denied RODC Password Replication Group HasMember  CN=Schema Admins,CN=Users,DC=domain,DC=local                         
    CN=Denied RODC Password Replication Group,CN=Users,DC=domain,DC=local Denied RODC Password Replication Group HasMember  CN=Domain Controllers,CN=Users,DC=domain,DC=local                    
    CN=Users,CN=Builtin,DC=domain,DC=local                                Users                                  HasMember  CN=Domain Users,CN=Users,DC=domain,DC=local                          
    CN=Guests,CN=Builtin,DC=domain,DC=local                               Guests                                 HasMember  CN=Domain Guests,CN=Users,DC=domain,DC=local                         
    CN=Domain Controllers,CN=Users,DC=domain,DC=local                     Domain Controllers                     IsMemberOf CN=Denied RODC Password Replication Group,CN=Users,DC=domain,DC=local
    CN=Domain Admins,CN=Users,DC=domain,DC=local                          Domain Admins                          IsMemberOf CN=Denied RODC Password Replication Group,CN=Users,DC=domain,DC=local
    CN=Group Policy Creator Owners,CN=Users,DC=domain,DC=local            Group Policy Creator Owners            IsMemberOf CN=Denied RODC Password Replication Group,CN=Users,DC=domain,DC=local
    CN=Read-only Domain Controllers,CN=Users,DC=domain,DC=local           Read-only Domain Controllers           IsMemberOf CN=Denied RODC Password Replication Group,CN=Users,DC=domain,DC=local
    CN=Cert Publishers,CN=Users,DC=domain,DC=local                        Cert Publishers                        IsMemberOf CN=Denied RODC Password Replication Group,CN=Users,DC=domain,DC=local
    CN=Schema Admins,CN=Users,DC=domain,DC=local                          Schema Admins                          IsMemberOf CN=Denied RODC Password Replication Group,CN=Users,DC=domain,DC=local
    CN=Enterprise Admins,CN=Users,DC=domain,DC=local                      Enterprise Admins                      IsMemberOf CN=Denied RODC Password Replication Group,CN=Users,DC=domain,DC=local
    CN=Domain Admins,CN=Users,DC=domain,DC=local                          Domain Admins                          IsMemberOf CN=Administrators,CN=Builtin,DC=domain,DC=local                      
    CN=Enterprise Admins,CN=Users,DC=domain,DC=local                      Enterprise Admins                      IsMemberOf CN=Administrators,CN=Builtin,DC=domain,DC=local                      
    CN=Domain Users,CN=Users,DC=domain,DC=local                           Domain Users                           IsMemberOf CN=Users,CN=Builtin,DC=domain,DC=local                               
    CN=Domain Guests,CN=Users,DC=domain,DC=local                          Domain Guests                          IsMemberOf CN=Guests,CN=Builtin,DC=domain,DC=local                              
    

    Of course you can limit it to just the distinguishedname and samaccountname if you like, but the above gives you more info at a glance.

    $groups = Get-ADGroup -Filter {GroupCategory -eq 'security'} -Properties member,memberof
    
    $groups.member | ForEach-Object {
        If ($groups.distinguishedname -contains $_) {
            $current=$_
            $groups | Where-Object {$_.member -contains $current} | Select-Object distinguishedname, samaccountname, @{Label='Type';Expression={"HasMember"}}, @{Label='RelativeGroup';Expression={$current}}
        }
    } | Select-Object distinguishedname, samaccountname -Unique
    
    $groups.memberof | ForEach-Object {
        If ($groups.distinguishedname -contains $_) {
            $current=$_
            $groups | Where-Object {$_.memberof -contains $current} | Select-Object distinguishedname, samaccountname, @{Label='Type';Expression={"IsMemberOf"}}, @{Label='RelativeGroup';Expression={$current}}
        }
    } | Select-Object distinguishedname, samaccountname -Unique
    
    
    Results:
    
    distinguishedname                                                     samaccountname                        
    -----------------                                                     --------------                        
    CN=Denied RODC Password Replication Group,CN=Users,DC=domain,DC=local Denied RODC Password Replication Group
    CN=Administrators,CN=Builtin,DC=domain,DC=local                       Administrators                        
    CN=Users,CN=Builtin,DC=domain,DC=local                                Users                                 
    CN=Guests,CN=Builtin,DC=domain,DC=local                               Guests                                
    CN=Domain Controllers,CN=Users,DC=domain,DC=local                     Domain Controllers                    
    CN=Domain Admins,CN=Users,DC=domain,DC=local                          Domain Admins                         
    CN=Group Policy Creator Owners,CN=Users,DC=domain,DC=local            Group Policy Creator Owners           
    CN=Read-only Domain Controllers,CN=Users,DC=domain,DC=local           Read-only Domain Controllers          
    CN=Cert Publishers,CN=Users,DC=domain,DC=local                        Cert Publishers                       
    CN=Schema Admins,CN=Users,DC=domain,DC=local                          Schema Admins                         
    CN=Enterprise Admins,CN=Users,DC=domain,DC=local                      Enterprise Admins                     
    CN=Domain Users,CN=Users,DC=domain,DC=local                           Domain Users                          
    CN=Domain Guests,CN=Users,DC=domain,DC=local                          Domain Guests                         
    
  • #46530

    Curtis Smith
    Participant

    Here is an example to query AD once and then work with the returned data

    $groups = Get-ADGroup -Filter {GroupCategory -eq 'security'} -Properties member,memberof
    
    $groups.member | ForEach-Object {
        If ($groups.distinguishedname -contains $_) {
            $current=$_
            $groups | Where-Object {$_.member -contains $current} | Select-Object distinguishedname, samaccountname, @{Label='Type';Expression={"HasMember"}}, @{Label='RelativeGroup';Expression={$current}}
        }
    } | Select-Object * -Unique
    
    $groups.memberof | ForEach-Object {
        If ($groups.distinguishedname -contains $_) {
            $current=$_
            $groups | Where-Object {$_.memberof -contains $current} | Select-Object distinguishedname, samaccountname, @{Label='Type';Expression={"IsMemberOf"}}, @{Label='RelativeGroup';Expression={$current}}
        }
    } | Select-Object * -Unique
    

    Results:

    distinguishedname                                                     samaccountname                         Type       RelativeGroup                                                  
    -----------------                                                     --------------                         ----       -------------                                                  
    CN=Denied RODC Password Replication Group,CN=Users,DC=domain,DC=local Denied RODC Password Replication Group HasMember  CN=Read-only Domain Controllers,CN=Users,DC=domain,DC=local          
    CN=Denied RODC Password Replication Group,CN=Users,DC=domain,DC=local Denied RODC Password Replication Group HasMember  CN=Group Policy Creator Owners,CN=Users,DC=domain,DC=local           
    CN=Denied RODC Password Replication Group,CN=Users,DC=domain,DC=local Denied RODC Password Replication Group HasMember  CN=Domain Admins,CN=Users,DC=domain,DC=local                         
    CN=Administrators,CN=Builtin,DC=domain,DC=local                       Administrators                         HasMember  CN=Domain Admins,CN=Users,DC=domain,DC=local                         
    CN=Denied RODC Password Replication Group,CN=Users,DC=domain,DC=local Denied RODC Password Replication Group HasMember  CN=Cert Publishers,CN=Users,DC=domain,DC=local                       
    CN=Denied RODC Password Replication Group,CN=Users,DC=domain,DC=local Denied RODC Password Replication Group HasMember  CN=Enterprise Admins,CN=Users,DC=domain,DC=local                     
    CN=Administrators,CN=Builtin,DC=domain,DC=local                       Administrators                         HasMember  CN=Enterprise Admins,CN=Users,DC=domain,DC=local                     
    CN=Denied RODC Password Replication Group,CN=Users,DC=domain,DC=local Denied RODC Password Replication Group HasMember  CN=Schema Admins,CN=Users,DC=domain,DC=local                         
    CN=Denied RODC Password Replication Group,CN=Users,DC=domain,DC=local Denied RODC Password Replication Group HasMember  CN=Domain Controllers,CN=Users,DC=domain,DC=local                    
    CN=Users,CN=Builtin,DC=domain,DC=local                                Users                                  HasMember  CN=Domain Users,CN=Users,DC=domain,DC=local                          
    CN=Guests,CN=Builtin,DC=domain,DC=local                               Guests                                 HasMember  CN=Domain Guests,CN=Users,DC=domain,DC=local                         
    CN=Domain Controllers,CN=Users,DC=domain,DC=local                     Domain Controllers                     IsMemberOf CN=Denied RODC Password Replication Group,CN=Users,DC=domain,DC=local
    CN=Domain Admins,CN=Users,DC=domain,DC=local                          Domain Admins                          IsMemberOf CN=Denied RODC Password Replication Group,CN=Users,DC=domain,DC=local
    CN=Group Policy Creator Owners,CN=Users,DC=domain,DC=local            Group Policy Creator Owners            IsMemberOf CN=Denied RODC Password Replication Group,CN=Users,DC=domain,DC=local
    CN=Read-only Domain Controllers,CN=Users,DC=domain,DC=local           Read-only Domain Controllers           IsMemberOf CN=Denied RODC Password Replication Group,CN=Users,DC=domain,DC=local
    CN=Cert Publishers,CN=Users,DC=domain,DC=local                        Cert Publishers                        IsMemberOf CN=Denied RODC Password Replication Group,CN=Users,DC=domain,DC=local
    CN=Schema Admins,CN=Users,DC=domain,DC=local                          Schema Admins                          IsMemberOf CN=Denied RODC Password Replication Group,CN=Users,DC=domain,DC=local
    CN=Enterprise Admins,CN=Users,DC=domain,DC=local                      Enterprise Admins                      IsMemberOf CN=Denied RODC Password Replication Group,CN=Users,DC=domain,DC=local
    CN=Domain Admins,CN=Users,DC=domain,DC=local                          Domain Admins                          IsMemberOf CN=Administrators,CN=Builtin,DC=domain,DC=local                      
    CN=Enterprise Admins,CN=Users,DC=domain,DC=local                      Enterprise Admins                      IsMemberOf CN=Administrators,CN=Builtin,DC=domain,DC=local                      
    CN=Domain Users,CN=Users,DC=domain,DC=local                           Domain Users                           IsMemberOf CN=Users,CN=Builtin,DC=domain,DC=local                               
    CN=Domain Guests,CN=Users,DC=domain,DC=local                          Domain Guests                          IsMemberOf CN=Guests,CN=Builtin,DC=domain,DC=local                              
    

    Of course you can limit it to just the distinguishedname and samaccountname if you like, but the above gives you more info at a glance.

    $groups = Get-ADGroup -Filter {GroupCategory -eq 'security'} -Properties member,memberof
    
    $groups.member | ForEach-Object {
        If ($groups.distinguishedname -contains $_) {
            $current=$_
            $groups | Where-Object {$_.member -contains $current} | Select-Object distinguishedname, samaccountname, @{Label='Type';Expression={"HasMember"}}, @{Label='RelativeGroup';Expression={$current}}
        }
    } | Select-Object distinguishedname, samaccountname -Unique
    
    $groups.memberof | ForEach-Object {
        If ($groups.distinguishedname -contains $_) {
            $current=$_
            $groups | Where-Object {$_.memberof -contains $current} | Select-Object distinguishedname, samaccountname, @{Label='Type';Expression={"IsMemberOf"}}, @{Label='RelativeGroup';Expression={$current}}
        }
    } | Select-Object distinguishedname, samaccountname -Unique
    

    Results:

    distinguishedname                                                     samaccountname                        
    -----------------                                                     --------------                        
    CN=Denied RODC Password Replication Group,CN=Users,DC=domain,DC=local Denied RODC Password Replication Group
    CN=Administrators,CN=Builtin,DC=domain,DC=local                       Administrators                        
    CN=Users,CN=Builtin,DC=domain,DC=local                                Users                                 
    CN=Guests,CN=Builtin,DC=domain,DC=local                               Guests                                
    CN=Domain Controllers,CN=Users,DC=domain,DC=local                     Domain Controllers                    
    CN=Domain Admins,CN=Users,DC=domain,DC=local                          Domain Admins                         
    CN=Group Policy Creator Owners,CN=Users,DC=domain,DC=local            Group Policy Creator Owners           
    CN=Read-only Domain Controllers,CN=Users,DC=domain,DC=local           Read-only Domain Controllers          
    CN=Cert Publishers,CN=Users,DC=domain,DC=local                        Cert Publishers                       
    CN=Schema Admins,CN=Users,DC=domain,DC=local                          Schema Admins                         
    CN=Enterprise Admins,CN=Users,DC=domain,DC=local                      Enterprise Admins                     
    CN=Domain Users,CN=Users,DC=domain,DC=local                           Domain Users                          
    CN=Domain Guests,CN=Users,DC=domain,DC=local                          Domain Guests                         
    
  • #47463

    Anthony Stringer
    Participant

    in my original example i got all the members of the groups and went through that list, but i think a shorter way is to just get all the security groups and see if they are a member of another security group.

    it ran exponentially faster than my last attempt, but you'll have to let me know if it gives problems for your environment

    EDIT: taking Curtis' information in mind (don't query AD multiple times), i have updated the script

    Write-Host 'getting all security groups...'
    $groups = Get-ADGroup -Filter "groupcategory -eq 'security'" -Properties memberof | select name, distinguishedname, memberof | ? memberof
    
    $securitygroups = $groups.distinguishedname
    
    # all groups progress
    $index = 0
    $total = $groups.Count
    $starttime = $lasttime = Get-Date
    
    $results = foreach ($group in $groups) {
        # all groups progress
        $index++
        $currtime = (Get-Date) - $starttime
        $avg = $currtime.TotalSeconds / $index
        $last = ((Get-Date) - $lasttime).TotalSeconds
        $left = $total - $index
        $WrPrgParam = @{
            Activity = (
                "ALL GROUPS $(Get-Date -f s)",
                "Total: $($currtime -replace '\..*')",
                "Avg: $('{0:N2}' -f $avg)",
                "Last: $('{0:N2}' -f $last)",
                "ETA: $('{0:N2}' -f ($avg * $left / 60))",
                "min ($([string](Get-Date).AddSeconds($avg*$left) -replace '^.* '))"
            ) -join ' '
            Status = "$index of $total ($left left) [$('{0:N2}' -f ($index / $total * 100))%]"
            CurrentOperation = "GROUP: $($group.name)"
            PercentComplete = $index / $total * 100
        }
        Write-Progress @WrPrgParam
        $lasttime = Get-Date
        
        $group | select name, @{n='memberof';e={$_.memberof | ? {$_ -in $securitygroups}}} | ? memberof
    }
    
    $results
    
  • #47468

    Curtis Smith
    Participant

    Anything that queries AD multiple times in a loop is going to be slower than querying once and working with the returned data. The example I posted shows how to do this and uses much less code to do it.

You must be logged in to reply to this topic.