2017-08-24 Don’t use Get-ADUser every time! Or maybe use?

3 minute read

When it comes to do some queries in the AD I often see that others advise

“Don’t call Get-ADUser every time. Better get everything local and query against it.”

Edit: I meant doing it in a script in a loop or something like that, not the daily interactive work. That it is faster to work with a local object then to do a Get-ADuser in a loop every time.

Putting the obvious greater AD load aside, how much slower is it?

Let’s test and compare.

Prepare

About 2000 user accounts in AD.

Test scenarios:

  • Get-ADUser each time
  • Get-ADUsers to a variable and then work with that object
  • Convert local results to a hashtable, arraylist, deserialize it with to and from csv, to and from json
$ADUsers = Get-ADUser -Filter *               # AD Results 
$ADUsersHT = @{}                              # HashTable
[collections.arraylist]$ADUsersArray = @{}    # ArrayList
#create ht and array
foreach ($aduser in $ADUsers){
	$ADUsersHT.Add($aduser.Samaccountname,$aduser)
	$null = $ADUsersArray.Add($aduser)
}
$ADusersCSV = $ADUsers | ConvertTo-CSV | ConvertFrom-Csv   # CSV
$ADUsersJSON = $AdusersCSV | ConvertTo-Json | ConvertFrom-Json # JSON

Get 100 random users from AD to be used in the loop

$users = @()
for ($i=0;$i -lt 100;$i++){
	$users += $adusers[$(Get-Random -Minimum 0 -Maximum ($ADUsers.Count - 1))].Samaccountname
}

Tests

Get user from AD by sAMAccountName

  • Normal Get-ADUser
    Get-ADUser $user
    
  • Where-Object on AD results
    $ADUsers | Where-Object samaccountname -EQ $user
    
  • Where method on AD results
    $ADUsers.Where({$_.samaccountname -EQ $user})
    
  • Where method with first 1 on AD results
    $ADUsersCSV.Where({$_.samaccountname -EQ $user},'First',1)
    
  • Foreach on AD results
    foreach ($aduser in $ADUsers){
      if ($aduser.samaccountname -eq $user){
      $aduser
      }
    }
    
  • Foreach with break on AD results
    foreach ($aduser in $ADUsers){
      if ($aduser.samaccountname -eq $user){
        $aduser					
        break
      }
    }
    
  • Where-Object on CSV results
    $ADUsersCSV | Where-Object samaccountname -EQ $user
    
  • Where method on CSV results
    $ADUsersCSV.Where({$_.samaccountname -EQ $user})
    
  • Where-Object on JSON results
    $ADUsersJSON | Where samaccountname -EQ $user}
    
  • Foreach on JSON
    foreach ($aduser in $ADUsersJSON){
      if ($aduser.samaccountname -eq $user){
        $aduser					      
      }
    }
    
  • Foreach with break on JSON
    foreach ($aduser in $ADUsersJSON){
      if ($aduser.samaccountname -eq $user){
        $aduser					
        break
      }
    }
    
  • Where-Object on ArrayList
    $ADusersArray | Where-Object samaccountname -EQ $user}
    
  • HashTable
    $ADUsersHT["$user"]
    

Results

Average results from 10 runs each 100 users in a loop. (Given time for 100 user queries)

  • Normal Get-ADUser - 800 ms
  • Where-Object on AD results - 9843 ms
  • Where method on AD results - 4895 ms
  • Where method with first 1 on AD results - 2526 ms
  • Foreach on AD results - 2066 ms
  • Foreach with break on AD results - 1094 ms
  • Where-Object on CSV results - 12571 ms
  • Where method on CSV results - 2877 ms
  • Where-Object on JSON results - 12513 ms
  • Foreach on JSON - 860 ms
  • Foreach with break on JSON - 463 ms
  • Where-Object on ArrayList - 12381 ms
  • HashTable - 7 ms

Summary

Worst times on Where-Object. It was very slow. ~ 11827 ms

  $ADUsers | Where samaccountname -EQ $user

The Where method was faster. ~ 3886 ms

  $ADUsersCSV.Where({$_.samaccountname -EQ $user})

It got better with First option. ~ 1948 ms

  $ADUsersCSV.Where({$_.samaccountname -EQ $user},'First',1)

Foreach, with break if applicable, is the right way to deal with objects if you already burned them in memory. 1094 ms

  foreach ($aduser in $ADUsers){
    if ($aduser.samaccountname -eq $user){
      $aduser					
      break
    }
  }

If a deserialized object is something you can live with then it gets a tick faster.

  $ADusersCSV = $ADUsers | ConvertTo-CSV | ConvertFrom-Csv
  foreach ($aduser in $ADUsersCSV){
    if ($aduser.samaccountname -eq $user){
      $aduser					
      break
    }
  } 

Still 860 ms and 463 ms with break on deserialized objects.

Why not stick to get the user direct with Get-Aduser if the AD guys are not knocking at your door? It is fast ~ 800 ms.

  Get-ADUser $user

But nothing beats a HashTable. It is super-fast - 7 ms.

It was 66 times faster than the best from the rest and 1795 times faster than the worst result.

  $ADUsersHT["$user"]

The HashTable is not something for every use case but is worth to consider when you want to speed up things.

Times

Source code

Code on pastebin

Join the Discussion

Reddit r/PowerShell

Leave a Comment