none
How do I speed up this query by ignoring computers off the network faster? RRS feed

  • Question

  • Good afternoon,

    I am running the below script to query the entire domain for local admins. Could anyone reccomend a way to speed this up by more quickly skipping computers that aren't on the network? Currently, every time it reaches a computer that is not on the network it hangs for up to 20 seconds (computers on the network return the data in less than a second). If I could decrease the ping time-out time, I could speed up this query tenfold.

    Script pasted below - Thank you!!!

    $Searcher = New-Object DirectoryServices.DirectorySearcher([ADSI]"")
    $Searcher.Filter = "(objectClass=computer)"
    $Computers = ($Searcher.Findall())
    md C:\Reports\IBX_Local_Admins_ALL
    Foreach ($Computer in $Computers)
    {
    $Path=$Computer.Path
    $Name=([ADSI]"$Path").Name
    write-host $Name
    $members =[ADSI]"WinNT://$Name/Administrators"
    $members = @($members.psbase.Invoke("Members"))
    $members | foreach {$_.GetType().InvokeMember("Name", 'GetProperty',
    $null, $_, $null) | out-file -append C:\Reports\IBX_Local_Admins_ALL\$name.txt
    }




    • Edited by BrianTTP Sunday, March 30, 2014 7:59 PM
    Sunday, March 30, 2014 7:58 PM

Answers

  • Here is how to do it with PowerShell and no simplistic linear processing.

    function Get-LocalAdmins{
        [CmdLetBinding()]
        Param(
            [Parameter(
                Mandatory=$true,
                ValueFromPipeline=$true,
                Position=0
            )]$computer
        )
        Process{
            Write-Verbose "Polling system: $computer"
            if(Test-Connection $Computer -quiet -count 1){
                $members =[ADSI]"WinNT://$computer/Administrators"
                $members.psbase.Invoke("Members") |
                    ForEach-Object{
                        New-Object PsObject -Property @{
                                Computer=$Computer
                                Domain=$_.GetType().InvokeMember('aDSPath', 'GetProperty',$null, $_, $null)
                                UserID=$_.GetType().InvokeMember('Name', 'GetProperty',$null, $_, $null)
            			}
                    }
        	}else{
               Write-Warning "System not found: $computer"  
        	}
    	}
    }
    
    $computers=([adsisearcher]'(objectClass=computer)').FindAll()|%{$_.Properties['name']}
    $computers | Get-LocalAdmins -verbose


    ¯\_(ツ)_/¯



    • Edited by jrv Sunday, March 30, 2014 9:38 PM
    • Marked as answer by BrianTTP Tuesday, April 1, 2014 9:41 PM
    Sunday, March 30, 2014 9:13 PM
  • This version will not fail no matter what is in the group.

    function Get-LocalAdmins{
        [CmdLetBinding()]
        Param(
            [Parameter(
                Mandatory=$true,
                ValueFromPipeline=$true,
                Position=0
            )]$computer
        )
        
        Process{
            Write-Verbose "Polling system: $computer"
            if(Test-Connection $Computer -quiet -count 1){
                $group=[ADSI]"WinNT://$computer/Administrators"
                $group.Invoke("Members") |
                    ForEach-Object{
                        New-Object PsObject -Property @{
                                Computer=$Computer
                                aDSPath=$_.GetType().InvokeMember('aDSPath', 'GetProperty',$null, $_, $null)
                                #UserID=$_.GetType().InvokeMember('Name', 'GetProperty',$null, $_, $null)
            			}
                    }
        	}else{
               Write-Warning "System not found: $computer"  
        	}
    	}
    }
    
    $computers=([adsisearcher]'(objectClass=computer)').FindAll() |%{$_.Properties['name']}
    $computers | Get-LocalAdmins -verbose
    
    


    ¯\_(ツ)_/¯

    • Marked as answer by BrianTTP Tuesday, April 1, 2014 9:41 PM
    Sunday, March 30, 2014 9:43 PM
  • You need to adjust the results.  Try this:

    $searcher=[adsisearcher]'(objectClass=computer)'
    $searcher.PageSize=500
    $searcher.PropertiesToLoad.Add('name')
    $computers=$searcher.FindAll()|%{$_.Properties['name']}
    $computers.count
    


    ¯\_(ツ)_/¯

    • Marked as answer by BrianTTP Tuesday, April 1, 2014 10:06 PM
    Tuesday, April 1, 2014 3:05 PM
  • Could I change the 500 to 0 for unlimited, or possibly to 20,000 to search everything?

    Thank you!

    Not how it works.  You will get back more if it is set less than the server limit. I believe in AD 2012 this may be changed to allow 0 to mean server adjusted and if server is set to default you will get 1000. Setting it higher just picks the limit.

    Most networks perform best at a setting of 500.  If you set it too high it actually slows the pipeline down.

    You can experiment.  Use Measure-Command to time various settings t find an optimal setting.

    AD is designed to limit long large queries.  If you don't set it you will get the server set limit.  Usually this is 1000.  Setting any value turns off the limit.  Setting a lower value allows the system to spool results more frequently and you get results faster.


    ¯\_(ツ)_/¯


    • Edited by jrv Tuesday, April 1, 2014 9:48 PM
    • Marked as answer by BrianTTP Tuesday, April 1, 2014 10:05 PM
    Tuesday, April 1, 2014 9:46 PM

All replies

  • Start like this:

    function Get-LocalAdmins{ Param( $Computer=$env:COMPUTERNAME ) if(Test-Connection $Computer -quiet -count 1){ $members =[ADSI]"WinNT://$computer/Administrators" $members.psbase.Invoke("Members") | ForEach-Object{ New-Object PsObject -Property @{                         Computer=$Computer
    Domain=$_.GetType().InvokeMember("Name", 'GetProperty',$null, $_, $null) UserID=$_.GetType().InvokeMember("Name", 'GetProperty',$null, $_, $null) } } }else{ Write-Host "System not found: $computer" -ForegroundColor red } }



    ¯\_(ツ)_/¯


    • Edited by jrv Sunday, March 30, 2014 8:54 PM
    Sunday, March 30, 2014 8:51 PM
  • Here is how to do it with PowerShell and no simplistic linear processing.

    function Get-LocalAdmins{
        [CmdLetBinding()]
        Param(
            [Parameter(
                Mandatory=$true,
                ValueFromPipeline=$true,
                Position=0
            )]$computer
        )
        Process{
            Write-Verbose "Polling system: $computer"
            if(Test-Connection $Computer -quiet -count 1){
                $members =[ADSI]"WinNT://$computer/Administrators"
                $members.psbase.Invoke("Members") |
                    ForEach-Object{
                        New-Object PsObject -Property @{
                                Computer=$Computer
                                Domain=$_.GetType().InvokeMember('aDSPath', 'GetProperty',$null, $_, $null)
                                UserID=$_.GetType().InvokeMember('Name', 'GetProperty',$null, $_, $null)
            			}
                    }
        	}else{
               Write-Warning "System not found: $computer"  
        	}
    	}
    }
    
    $computers=([adsisearcher]'(objectClass=computer)').FindAll()|%{$_.Properties['name']}
    $computers | Get-LocalAdmins -verbose


    ¯\_(ツ)_/¯



    • Edited by jrv Sunday, March 30, 2014 9:38 PM
    • Marked as answer by BrianTTP Tuesday, April 1, 2014 9:41 PM
    Sunday, March 30, 2014 9:13 PM
  • We can also set it up now to be executed as a set of jobs.  The jobs will complete an a minute or less assuming your network is working correctly.

    We can scan 10,000 computers in a short few minutes.


    ¯\_(ツ)_/¯

    Sunday, March 30, 2014 9:15 PM
  • Your code will fail in a domain due to the use of Domain Admins group in the list.  It is not a user object.

    That can be fixed very easily.


    ¯\_(ツ)_/¯

    Sunday, March 30, 2014 9:36 PM
  • This version will not fail no matter what is in the group.

    function Get-LocalAdmins{
        [CmdLetBinding()]
        Param(
            [Parameter(
                Mandatory=$true,
                ValueFromPipeline=$true,
                Position=0
            )]$computer
        )
        
        Process{
            Write-Verbose "Polling system: $computer"
            if(Test-Connection $Computer -quiet -count 1){
                $group=[ADSI]"WinNT://$computer/Administrators"
                $group.Invoke("Members") |
                    ForEach-Object{
                        New-Object PsObject -Property @{
                                Computer=$Computer
                                aDSPath=$_.GetType().InvokeMember('aDSPath', 'GetProperty',$null, $_, $null)
                                #UserID=$_.GetType().InvokeMember('Name', 'GetProperty',$null, $_, $null)
            			}
                    }
        	}else{
               Write-Warning "System not found: $computer"  
        	}
    	}
    }
    
    $computers=([adsisearcher]'(objectClass=computer)').FindAll() |%{$_.Properties['name']}
    $computers | Get-LocalAdmins -verbose
    
    


    ¯\_(ツ)_/¯

    • Marked as answer by BrianTTP Tuesday, April 1, 2014 9:41 PM
    Sunday, March 30, 2014 9:43 PM
  • This version will not fail no matter what is in the group.

    function Get-LocalAdmins{
        [CmdLetBinding()]
        Param(
            [Parameter(
                Mandatory=$true,
                ValueFromPipeline=$true,
                Position=0
            )]$computer
        )
        
        Process{
            Write-Verbose "Polling system: $computer"
            if(Test-Connection $Computer -quiet -count 1){
                $group=[ADSI]"WinNT://$computer/Administrators"
                $group.Invoke("Members") |
                    ForEach-Object{
                        New-Object PsObject -Property @{
                                Computer=$Computer
                                aDSPath=$_.GetType().InvokeMember('aDSPath', 'GetProperty',$null, $_, $null)
                                #UserID=$_.GetType().InvokeMember('Name', 'GetProperty',$null, $_, $null)
            			}
                    }
        	}else{
               Write-Warning "System not found: $computer"  
        	}
    	}
    }
    
    $computers=([adsisearcher]'(objectClass=computer)').FindAll() |%{$_.Properties['name']}
    $computers | Get-LocalAdmins -verbose
    


    ¯\_(ツ)_/¯

    Hi JRV,

    Thank you for your help so far! When I ran the above version, it seemed to be going fine but it stopped at a certain point saying the script was "successful", but it only queried 148 computers. I should note that it is also the same 148 computers each time I run it. No errors, it just stops and says successful. Any ideas?

    TY!

    Tuesday, April 1, 2014 1:15 PM
  • Why would it work any differently?  You obviously have on that many computers.

    There is also nothing in my script that says "successful".  Perhaps you are running someone else's script. Perhaps you made changes to the script.

    Perhaps you have warnings turned off globally.


    ¯\_(ツ)_/¯

    Tuesday, April 1, 2014 1:56 PM
  • I copied the script word for word. It should be querying the whole domain, correct? We have over 10,000 PCs in Active Directory :)

    Tuesday, April 1, 2014 2:15 PM
  • You need to adjust the results.  Try this:

    $searcher=[adsisearcher]'(objectClass=computer)'
    $searcher.PageSize=500
    $searcher.PropertiesToLoad.Add('name')
    $computers=$searcher.FindAll()|%{$_.Properties['name']}
    $computers.count
    


    ¯\_(ツ)_/¯

    • Marked as answer by BrianTTP Tuesday, April 1, 2014 10:06 PM
    Tuesday, April 1, 2014 3:05 PM
  • Could I change the 500 to 0 for unlimited, or possibly to 20,000 to search everything?

    Thank you!

    Tuesday, April 1, 2014 9:41 PM
  • Could I change the 500 to 0 for unlimited, or possibly to 20,000 to search everything?

    Thank you!

    Not how it works.  You will get back more if it is set less than the server limit. I believe in AD 2012 this may be changed to allow 0 to mean server adjusted and if server is set to default you will get 1000. Setting it higher just picks the limit.

    Most networks perform best at a setting of 500.  If you set it too high it actually slows the pipeline down.

    You can experiment.  Use Measure-Command to time various settings t find an optimal setting.

    AD is designed to limit long large queries.  If you don't set it you will get the server set limit.  Usually this is 1000.  Setting any value turns off the limit.  Setting a lower value allows the system to spool results more frequently and you get results faster.


    ¯\_(ツ)_/¯


    • Edited by jrv Tuesday, April 1, 2014 9:48 PM
    • Marked as answer by BrianTTP Tuesday, April 1, 2014 10:05 PM
    Tuesday, April 1, 2014 9:46 PM
  • Thank you again! Our server is set to 1000, so I will try 500. I'll update with results. You are awesome!
    Tuesday, April 1, 2014 10:05 PM
  • Don't take my word or anyone else's for the absolute. Look up and track down all technical rules personally.


    ¯\_(ツ)_/¯

    Wednesday, April 2, 2014 1:02 AM
  • Don't take my word or anyone else's for the absolute. Look up and track down all technical rules personally.


    ¯\_(ツ)_/¯

    Great advice! I wanted to say that adding the $searcher.pagesize changes worked!

    It's now running with no problems at all, already made it through 8,000 Windows computer objects (and counting).

    Thanks very much!

    Wednesday, April 2, 2014 2:56 PM
  • Using jobs it would have been done in a couple of minutes.

    ¯\_(ツ)_/¯

    Wednesday, April 2, 2014 3:22 PM