Get-QADUser vs Get-ADUser

I'm just wondering why there seems to be such a massive performance difference between the Get-QADuser and Get-ADuser.  I know the QAD commands return a lot more data and I have to use them when I need access to the virtual attributes defined in my environment but the speed difference is a millennium apart.  

This takes 6 seconds - measure-command { Get-ADUser    -LdapFilter '(&(sAMAccountType=805306368)(!(|(userAccountControl:1.2.840.113556.1.4.803:=2)(employeeID=*)(employeeNumber=*))))' }  
This takes 2 minutes  - measure-command { Get-QADUser -LdapFilter '(&(sAMAccountType=805306368)(!(|(userAccountControl:1.2.840.113556.1.4.803:=2)(employeeID=*)(employeeNumber=*))))'     -proxy -SizeLimit 0 }  

2 minutes seems like a lifetime when debugging :-) 

I've taken to loading the AD commandlets more and more despite feeling that they are somewhat inferior to the Quest commandlets but convincing others that they shoul dbe using them is pretty hard when the first script they write using them runs so sloooooooly.


  • Your example is an interesting one because I have always believed that the main reason why the Quest cmdlets are slower is that they implement a more sophisticated (?) name resolution algorithm.  That is to say that when you specify the '-Identity' switch, the cmdlet performs a more thorough search for the object (examines more name-related attributes).  As you have probably experienced, this can have unintended consequences with more objects being returned than you expect in some cases.

    But back to your example...

    You are using a straight-up LDAP query which by my way of thinking would bypass the mechanism I described above so like you, I am surprised by the difference in performance.

    For the very best performance in Active Roles embedded scripts, I often use the ADSI coding approach.  It's very arcane and unfriendly to work with syntax-wise but it is fast.

    Day to day though, like you I will often get use Get-ADUser.

    I maintain though that especially for beginner scripters who just need to get something done, the Quest cmdlets are much friendlier to use both because of the way they implement switches for searching and setting attributes AND because of the additional calculated properties they return.  'Plus they give you a handy way to send Controls to the AR service.Relaxed

  • I have exactly the same sentiments, I just don't understand why they are so much slower and mine is a small environment I can imagine the speed being a real issue in a 100,000 plus user environment.  If I didn't have such a dependence on the ARS virtual attributes I use in my environment I would probably stop using them at all, which is a shame as they are much more feature rich and user friendly and in a multidomain environment allow management of all environments from a single prompt!  

  • You might not have to stop using them for the AR cmdlets don't have to connect through Active Roles. Try eliminating the -Proxy parameter and see if they are on pace with the native AD cmdlets.

  • Hey good catch!  I missed the fact that Lee used the -proxy switch.  That does make quite a difference.  The Quest cmdlets are still slower though.

  • Hey Lee - for the QADUser setup - have you already established a connect-qadservice -proxy in the same Powershell Session? Or is your command here also establishing that on first running?  Does it run faster a second time ?  Also are your ARS server you are connected with best geographically (or WAN connected) specified with DIRSYNC setting to an AD DC thats closest?

  • I re-jigged Lee's command lines for my environment as shown and set the the "Quest cmdlet Progress Policy" NOT to display the progress bar.

    measure-command { Get-ADUser  -LdapFilter '(&(sAMAccountType=805306368)(!(|(userAccountControl:1.2.840.113556.1.4.803:=2))))' }

    measure-command { Get-QADUser -DontUseDefaultIncludedProperties -DontConvertValuesToFriendlyRepresentation -LdapFilter '(&(sAMAccountType=805306368)(!(|(userAccountControl:1.2.840.113556.1.4.803:=2))))'    -SizeLimit 0 }  


    For a small 3500 user AD, I obtained the following results:

    Seconds : 12
    Milliseconds : 951
    Ticks : 129511830
    TotalDays : 0.000149897951388889
    TotalHours : 0.00359755083333333
    TotalMinutes : 0.21585305
    TotalSeconds : 12.951183
    TotalMilliseconds : 12951.183

    Seconds : 27
    Milliseconds : 603
    Ticks : 276037552
    TotalDays : 0.000319487907407407
    TotalHours : 0.00766770977777778
    TotalMinutes : 0.460062586666667
    TotalSeconds : 27.6037552
    TotalMilliseconds : 27603.7552

  • This code returns a similar base property set to Get-ADUser (you can see the list) and takes 9 MILLISECONDS:

    Measure-Command {

    # Set the searcher query

    $searcher=[adsisearcher]'(&(sAMAccountType=805306368)(!(|(userAccountControl:1.2.840.113556.1.4.803:=2))))'

    # Add the properties we want to return

    $colProplist = "samaccountname","distinguishedname","objectguid","Name"

    foreach ($i in $colPropList) { $searcher.PropertiesToLoad.Add($i) | out-null }

    # Execute the query

    $Users = $searcher.findall()

    }

     

  • clear-host
    $t1 = measure-command { $users1 = Get-ADUser -LdapFilter '(&(sAMAccountType=805306368)(!(|(userAccountControl:1.2.840.113556.1.4.803:=2))))' }
    $t2 = measure-command { $users2 = Get-QADUser -DontUseDefaultIncludedProperties -DontConvertValuesToFriendlyRepresentation -LdapFilter '(&(sAMAccountType=805306368)(!(|(userAccountControl:1.2.840.113556.1.4.803:=2))))' -SizeLimit 0 -Proxy:$false }
    $t3 = measure-command { $users3 = Get-QADUser -DontUseDefaultIncludedProperties -DontConvertValuesToFriendlyRepresentation -LdapFilter '(&(sAMAccountType=805306368)(!(|(userAccountControl:1.2.840.113556.1.4.803:=2))))' -SizeLimit 0 -Proxy }
    $t4 = measure-command { $users4 = Get-QADUser -LdapFilter '(&(sAMAccountType=805306368)(!(|(userAccountControl:1.2.840.113556.1.4.803:=2))))' -SizeLimit 0 -Proxy:$false }
    $t5 = measure-command { $users5 = Get-QADUser -LdapFilter '(&(sAMAccountType=805306368)(!(|(userAccountControl:1.2.840.113556.1.4.803:=2))))' -SizeLimit 0 -proxy }
    write-host "AD commandlet runtime .......................: $($t1.Minutes) Minutes $($t1.Seconds) seconds"
    write-host "QAD commandlet runtime no defaults...........: $($t2.Minutes) Minutes $($t2.Seconds) seconds"
    write-host "QAD commandlet runtime no defaults w proxy ..: $($t3.Minutes) Minutes $($t3.Seconds) seconds"
    write-host "QAD commandlet runtime ......................: $($t4.Minutes) Minutes $($t4.Seconds) seconds"
    write-host "QAD commandlet runtime w proxy ..............: $($t5.Minutes) Minutes $($t5.Seconds) seconds"


    and the score on the door is :

    AD commandlet runtime ......................................: 0 Minutes 9 seconds
    QAD commandlet runtime no defaults..................: 0 Minutes 13 seconds
    QAD commandlet runtime no defaults w proxy ...: 1 Minutes 53 seconds
    QAD commandlet runtime ...................................: 1 Minutes 46 seconds
    QAD commandlet runtime w proxy ......................: 2 Minutes 9 seconds


    Given in an ARS environment I HAVE to use the proxy switch I'm still disappointed with the performance.  It's using an SQL backend and I'd expect similar performance when excluding all the default parameters possibly faster after all the SQL server only has the ARS client querying it.  

  • yes the proxy connection is already established and multiple runs take the same time - I'm actually running the command on the ARS host so geographically you can't get nearer.  The DirSync servers are in the same Data Centre

  • Looks like my responses may be being blocked as spam for some reason so here goes again....

    clear-host
    $t1 = measure-command { $users1 = Get-ADUser -LdapFilter '(&(sAMAccountType=805306368)(!(|(userAccountControl:1.2.840.113556.1.4.803:=2))))' }
    $t2 = measure-command { $users2 = Get-QADUser -DontUseDefaultIncludedProperties -DontConvertValuesToFriendlyRepresentation -LdapFilter '(&(sAMAccountType=805306368)(!(|(userAccountControl:1.2.840.113556.1.4.803:=2))))' -SizeLimit 0 -Proxy:$false }
    $t3 = measure-command { $users3 = Get-QADUser -DontUseDefaultIncludedProperties -DontConvertValuesToFriendlyRepresentation -LdapFilter '(&(sAMAccountType=805306368)(!(|(userAccountControl:1.2.840.113556.1.4.803:=2))))' -SizeLimit 0 -Proxy }
    $t4 = measure-command { $users4 = Get-QADUser -LdapFilter '(&(sAMAccountType=805306368)(!(|(userAccountControl:1.2.840.113556.1.4.803:=2))))' -SizeLimit 0 -Proxy:$false }
    $t5 = measure-command { $users5 = Get-QADUser -LdapFilter '(&(sAMAccountType=805306368)(!(|(userAccountControl:1.2.840.113556.1.4.803:=2))))' -SizeLimit 0 -proxy }
    write-host "AD commandlet runtime .......................: $($t1.Minutes) Minutes $($t1.Seconds) seconds"
    write-host "QAD commandlet runtime no defaults...........: $($t2.Minutes) Minutes $($t2.Seconds) seconds"
    write-host "QAD commandlet runtime no defaults w proxy ..: $($t3.Minutes) Minutes $($t3.Seconds) seconds"
    write-host "QAD commandlet runtime ......................: $($t4.Minutes) Minutes $($t4.Seconds) seconds"
    write-host "QAD commandlet runtime w proxy ..............: $($t5.Minutes) Minutes $($t5.Seconds) seconds"


    and the results....

    AD commandlet runtime .......................: 0 Minutes 9 seconds
    QAD commandlet runtime no defaults...........: 0 Minutes 13 seconds
    QAD commandlet runtime no defaults w proxy ..: 1 Minutes 53 seconds
    QAD commandlet runtime ......................: 1 Minutes 46 seconds
    QAD commandlet runtime w proxy ..............: 2 Minutes 9 seconds

    Its a poor showing when using the proxy switch especially as it's an SQL backend - which should be equally as fast as AD in responding if not faster.

    As I'm using virtual attributes I have no choice but to use the PROXY switch.