find member,memberOf for Groups only

Tagged: 

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

Viewing 9 posts - 1 through 9 (of 9 total)
  • Author
    Posts
  • #46368
    Profile photo of Jeff Taylor
    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
    Profile photo of Anthony Stringer
    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
    Profile photo of Craig Duff
    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.

    #46528
    Profile photo of Curtis Smith
    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
    Profile photo of Curtis Smith
    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                         
    
    #47403
    Profile photo of Jeff Taylor
    Jeff Taylor
    Participant

    ~

    • This reply was modified 2 months, 1 week ago by Profile photo of Jeff Taylor Jeff Taylor.
    #47406
    Profile photo of Jeff Taylor
    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

    #47463
    Profile photo of Anthony Stringer
    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
    Profile photo of Curtis Smith
    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.

Viewing 9 posts - 1 through 9 (of 9 total)

You must be logged in to reply to this topic.