none
need to find nested groups in groups, my way is way to slow.

    Question

  • currently i am running this script.

                   

    get-content d:\names.txt | where {$_} | foreach {
     $GroupName =$_
     Get-QADGroupMember -Service domain.com -sizelimit 0 $_ -Indirect -Type group | select name,type,@{n="GroupName";e={$GroupName}}
    } | Export-Csv -NoTypeInformation d:\members.csv

    this is taking 6+hrs so far to run is there anyway to speed this up? the quest cmdlets are nice but are extremely slow in a circumstance like this.  I am parsing 3600 groups looking for ones that have groups nested in them, I know it should take a while but from past experience i found directorysearcher to be way faster and i'm looking for a way to make this quicker/less painful. This is a remote domain and i don't have access to run say a dsget group -member  feeding from an input file on the DC.

    Regards.

    Wednesday, February 29, 2012 8:14 PM

Answers

All replies

  • Not tested, but if the objective is just to find groups that have another group nested in them you shouldn't need to do the indirect search on every group.  Just do a single retrieval of all the group DNs, then do a shallow search of membership of the groups on your list and check those against the cached group dns:

    $groupnames = get-qadgroup -resultsize 0 | select -expand dn
    foreach ($name in (get-content d:\names.txt | where {$_})) {
     
     Get-QADGroupMember -Service domain.com -sizelimit 0 $name |
     foreach {if ($groupnames -contains $_.dn){"Found $($_.dn) nested in $name"}
    }


    [string](0..33|%{[char][int](46+("686552495351636652556262185355647068516270555358646562655775 0645570").substring(($_*2),2))})-replace " "


    • Edited by mjolinor Wednesday, February 29, 2012 8:50 PM
    • Proposed as answer by Richard MuellerMVP Wednesday, February 29, 2012 8:54 PM
    Wednesday, February 29, 2012 8:48 PM
  • Miss a paren.  Updated.

    [string](0..33|%{[char][int](46+("686552495351636652556262185355647068516270555358646562655775 0645570").substring(($_*2),2))})-replace " "

    Wednesday, February 29, 2012 8:51 PM
  • where would i add the export?
    Wednesday, February 29, 2012 8:57 PM
  • Agreed. Group nesting can be cyclical (i.e. A is a member of B, B is a member of A) without aparently impacting AD's ability to resolve permissions. But it can wreak havoc with a group membership tracing script that does not recognize "hey, I have already been here", resulting in an infinite loop.

    Al Dunbar

    Wednesday, February 29, 2012 8:59 PM
  • You'd need to create objects from the results, and then export those.  That isn't going to give you exactly the same kind of results you had before.  It will produce a list of group dn/group dn pairs of groups it found nested in other groups.  How do you want to label those columns?

    [string](0..33|%{[char][int](46+("686552495351636652556262185355647068516270555358646562655775 0645570").substring(($_*2),2))})-replace " "

    Wednesday, February 29, 2012 9:04 PM
  • Using adsi:

    Function Get-NestedGroups($group)
    {  
        $group.member | Foreach {  
    	    $gr = [ADSI]"LDAP://$_"  
    	    if ($gr.objectclass -eq 'group') {  
    		  $gr
    	      Get-NestedGroups $gr  
    	    }  
        }  
    } 
    
    $groupType = @{
    		-2147483644 = "S_DL" 
    		-2147483646 = "S_GL" 
    		-2147483640 = "S_U"  
    		4			= "D_DL" 
    		2			= "D_GL" 
    		8			= "D_U"  
    }
    
    get-content d:\names.txt | where {$_} | foreach {
     		$searcher = [adsisearcher]"(&(objectCategory=group)(name=$_))"
    		$searcher.SearchRoot = [adsi]"LDAP://DC=Domain,DC=Com"
    		$group = $searcher.FindOne()
    		if($group)
    		{
    			$group = [ADSI]$group.Path
    			Get-NestedGroups $group | select @{n="name";e={[string]$_.name}},
    			@{n="type";e={$grouptype[$_.grouptype]}},
    			@{n="GroupName";e={[string]$group.name}}
    		}
    } | Export-Csv -NoType C:\result.csv

    Wednesday, February 29, 2012 9:10 PM
  • You'd need to create objects from the results, and then export those.  That isn't going to give you exactly the same kind of results you had before.  It will produce a list of group dn/group dn pairs of groups it found nested in other groups.  How do you want to label those columns?

    [string](0..33|%{[char][int](46+("686552495351636652556262185355647068516270555358646562655775 0645570").substring(($_*2),2))})-replace

    basically col_a primary group and col_b whats nested

    Wednesday, February 29, 2012 10:09 PM
  • Using adsi:

    Function Get-NestedGroups($group)
    {  
        $group.member | Foreach {  
    	    $gr = [ADSI]"LDAP://$_"  
    	    if ($gr.objectclass -eq 'group') {  
    		  $gr
    	      Get-NestedGroups $gr  
    	    }  
        }  
    } 
    
    $groupType = @{
    		-2147483644 = "S_DL" 
    		-2147483646 = "S_GL" 
    		-2147483640 = "S_U"  
    		4			= "D_DL" 
    		2			= "D_GL" 
    		8			= "D_U"  
    }
    
    get-content d:\names.txt | where {$_} | foreach {
     		$searcher = [adsisearcher]"(&(objectCategory=group)(name=$_))"
    		$searcher.SearchRoot = [adsi]"LDAP://DC=Domain,DC=Com"
    		$group = $searcher.FindOne()
    		if($group)
    		{
    			$group = [ADSI]$group.Path
    			Get-NestedGroups $group | select @{n="name";e={[string]$_.name}},
    			@{n="type";e={$grouptype[$_.grouptype]}},
    			@{n="GroupName";e={[string]$group.name}}
    		}
    } | Export-Csv -NoType C:\result.csv

    ill give this a go tomorrow also thanks
    Wednesday, February 29, 2012 10:10 PM
  • $groupnames = get-qadgroup -resultsize 0 | select -expand dn
    &{foreach ($name in (get-content d:\names.txt | where {$_})) {
     
     Get-QADGroupMember -Service domain.com -sizelimit 0 $name |
        foreach {if ($groupnames -contains $_.dn){
            new-object psobject -property @{Group = $_.dn;NestedIn = $name}
            }
        }
     }
     } | export-csv -NoTypeInformation d:\members.csv


    [string](0..33|%{[char][int](46+("686552495351636652556262185355647068516270555358646562655775 0645570").substring(($_*2),2))})-replace " "

    Wednesday, February 29, 2012 10:26 PM
  • running now... still taking its time so we shall see today how it goes.  the ADSI one seems to run really quick but errors out with 

    The script failed due to call depth overflow.  The call depth reached 1001 and the maximum is 1000.
        + CategoryInfo          : InvalidOperation: (1001:Int32) [], ParentContainsErrorRecordException
        + FullyQualifiedErrorId : CallDepthOverflow

    Thursday, March 01, 2012 12:39 PM
  • $groupnames = get-qadgroup -resultsize 0 | select -expand dn
    &{foreach ($name in (get-content d:\names.txt | where {$_})) {
     
     Get-QADGroupMember -Service domain.com -sizelimit 0 $name |
        foreach {if ($groupnames -contains $_.dn){
            new-object psobject -property @{Group = $_.dn;NestedIn = $name}
            }
        }
     }
     } | export-csv -NoTypeInformation d:\members.csv


    [string](0..33|%{[char][int](46+("686552495351636652556262185355647068516270555358646562655775 0645570").substring(($_*2),2))})-replace " "

    been running over 4hrs now...
    Thursday, March 01, 2012 3:56 PM
  • Is the 3600 the number of groups in your list, or the number of groups in that domain?


    [string](0..33|%{[char][int](46+("686552495351636652556262185355647068516270555358646562655775 0645570").substring(($_*2),2))})-replace " "

    Thursday, March 01, 2012 4:36 PM
  • in the list...
    Thursday, March 01, 2012 4:37 PM
  • How many are in the domain?

    [string](0..33|%{[char][int](46+("686552495351636652556262185355647068516270555358646562655775 0645570").substring(($_*2),2))})-replace " "

    Thursday, March 01, 2012 4:38 PM
  • 3927... but i only need it for global groups.
    Thursday, March 01, 2012 4:40 PM
  • You only need to know which global groups have any nested group, you only need to know which groups (of any type) have a global group nested, or which global groups have global groups nested?

    This should be faster than the other script, but I have no idea if it will give you the results you want:

    &{foreach ($name in (get-content d:\names.txt | where {$_})) {
     
     Get-QADGroupMember -Service domain.com -sizelimit 0 $name -type Group |
       foreach {
        new-object psobject -property @{Group = $_.dn;NestedIn = $name}
            }
        }
     }| export-csv -NoTypeInformation d:\members.csv

    [string](0..33|%{[char][int](46+("686552495351636652556262185355647068516270555358646562655775 0645570").substring(($_*2),2))})-replace " "


    • Edited by mjolinor Thursday, March 01, 2012 4:55 PM
    Thursday, March 01, 2012 4:54 PM
  • still slow...    I think if the ADSI script above would do it if i could fix the error, it would be faster.. as it returns the 1200 usres in the csv before it dies, and it does that in like 2 minutes.
    Thursday, March 01, 2012 7:53 PM
  • You only need to know which global groups have any nested group, you only need to know which groups (of any type) have a global group nested, or which global groups have global groups nested?

    This should be faster than the other script, but I have no idea if it will give you the results you want:

    &{foreach ($name in (get-content d:\names.txt | where {$_})) {
     
     Get-QADGroupMember -Service domain.com -sizelimit 0 $name -type Group |
       foreach {
        new-object psobject -property @{Group = $_.dn;NestedIn = $name}
            }
        }
     }| export-csv -NoTypeInformation d:\members.csv

    [string](0..33|%{[char][int](46+("686552495351636652556262185355647068516270555358646562655775 0645570").substring(($_*2),2))})-replace " "


    this ran over 12hrs...
    Friday, March 02, 2012 12:15 PM
  • I don't quite understand that. I'd think it would have been faster because it's only doing a shallow enumeration of the members.

    [string](0..33|%{[char][int](46+("686552495351636652556262185355647068516270555358646562655775 0645570").substring(($_*2),2))})-replace " "

    Friday, March 02, 2012 12:20 PM
  • I find the quest snapins are good for small batches but when it comes to large jobs like this either directory searcher or adsi are way quicker... its not a 2008 domain so i cant even use the native AD cmdlets...
    Friday, March 02, 2012 12:51 PM
  • If you could get remoting enabled on one of those DCs, you could use dsquery in a remote session.

    [string](0..33|%{[char][int](46+("686552495351636652556262185355647068516270555358646562655775 0645570").substring(($_*2),2))})-replace " "

    Friday, March 02, 2012 12:55 PM
  • any insight on the error the adsi script above is?
    Friday, March 02, 2012 12:56 PM
  • It looks like you may have a circular reference in one of your groups that putting it into an infinite recursion loop.

    [string](0..33|%{[char][int](46+("686552495351636652556262185355647068516270555358646562655775 0645570").substring(($_*2),2))})-replace " "

    Friday, March 02, 2012 1:32 PM
  • could that be limited to just the 3 security types instead of including the distribution lists.
    Friday, March 02, 2012 2:01 PM
  • Here is a script I wrote to find all instances of circular nested groups in the domain:

    http://gallery.technet.microsoft.com/fa4ccf4f-712e-459c-88b4-aacdb03a08d0

    If you have circular nested groups, either correct the situation, or when you recursively enumerate group membership you need to track group members in a hash table so you can recognize duplicate groups and not expand them.


    Richard Mueller - MVP Directory Services

    Friday, March 02, 2012 5:44 PM