none
Query using AD Module Timed Out RRS feed

  • Question

  • Hi all,

    Most of us are aware about the two minute timeout for queries using AD Module in PS.  And this timeout is unchangeable.

    However sometimes a time consuming query is unavoidable.  Imagine this scenario:

    There are 500,000 users in one OU in which all accounts except maybe two dozens have account expiry date.  My task is to find them out and put an expiry date on them.  My code is like this:

    Get-ADUser -SearchBase:'OU=MyOU,DC=MyDomain,DC=COM' -RessultPageSize:10 -ResultSetSize:$null -LDAPFilter:"(|(accountExpires=9223372036854775807)(accountExpires=0))" | Set-ADUser -AccountExpirationDate:$date

    However it just keeps timing out:

    Get-ADUser : This operation returned because the timeout period expired
    At line:1 char:11
    + Get-ADUser <<<<  -SearchBase:'OU=MyOU,DC=MyDomain,DC=COM
    ' -ResultPageSize:10 -ResultSetSize:$null -LDAPFilter:"(|(accountExpires=922337
    2036854775807)(accountExpires=0))" | Measure-Object
        + CategoryInfo          : NotSpecified: (:) [Get-ADUser], ADException
        + FullyQualifiedErrorId : This operation returned because the timeout peri
       od expired,Microsoft.ActiveDirectory.Management.Commands.GetADUser

    This I understand.  The job basically have to scan through thousands of objects with negative results, and it times out well before the first positive is yielded.

    So does this mean I can't do this with AD Module and have to resort to something like SDS?



    Thanks.

    Friday, January 4, 2013 7:44 PM

Answers

  • Your query looks fine to me (other than RessultPageSize mis-spelled). Get-ADUser already filters on user objects. I don't believe your filter can be improved. However, I find Page Size 200 best in my domain with 2300 users. I would expect Page Size 500 or even 1000 would be faster. And it might help to use Get-ADObject simply because it only retrieves 4 default properties (as opposed to Get-ADUser that retrieves 10), and add the clauses (&(objectCategory=person)(objectClass=user)) to restrict to user objects.

    The basic problems is that the AD modules are slow, several times slower that PowerShell V1 methods. Most of the time it doesn't matter, but I guess 500,000 users is too much here. I would expect the fastest method would use command line utilities (dsquery * piped to dsmod), next fastest VBScript, next PowerShell V1, and slowest the AD module cmdlets. Also, VBScript and PowerShell V1 allow you to assign a timeout. A PowerShell V1 method (partially tested) could be similar to below:

    $domain = New-Object DirectoryServices.DirectoryEntry("LDAP://ou=MyOU,dc=MyDomain,dc=com")
    $searcher = New-Object System.DirectoryServices.DirectorySearcher
    $searcher.SearchRoot = $domain
    $Searcher.PageSize = 1000
    $Searcher.SearchScope = "subtree"

    # Filter on user members of the specified group.
    $Searcher.Filter = "(&(objectCategory=person)(objectClass=user)(|(accountExpires=9223372036854775807)(accountExpires=0)))"

    # Specify attributes to retrieve.
    $Searcher.PropertiesToLoad.Add("distinguishedName") > $Null

    $Results = $Searcher.FindAll()

    $NewDate = [DateTime]"January 1, 2014"

    ForEach($Result in $Results)
    {

        $DN = $Result.Properties.Item("distinguishedName")
        $User = [ADSI]"LDAP://$DN"
        $UserType = $User.GetType()
        $UserType.InvokeMember("AccountExpirationDate", "InvokeMethod", $Null, $User, $NewDate)

    }

    -----

    To specify a timeout, I believe you must use ADO, which might be a bit slower.


    Richard Mueller - MVP Directory Services


    Friday, January 4, 2013 11:15 PM

All replies

  • IIRC, the two minute timeout is due to an AD setting and not the PS AD module. See NTDSUTIL LDAP policy settings MaxQueryDuration in http://support.microsoft.com/kb/315071

    For your LDAP filter, try two things: 1) remove ResultPageSize and ResultSetSize, thus using AD's default values and 2) add an indexed attribute, such as objectCategory=Person.

    For more information, see http://social.technet.microsoft.com/Forums/en/winservergen/thread/a3156ae6-3362-48e4-a872-286a624e0b1c

    I like using Joeware's ADFIND.exe utility with the STATS control as it provides timing and filter results when I am testing LDAP filters.


    BrianY MCT, MCLC


    • Edited by BrianYx2 Friday, January 4, 2013 8:35 PM
    Friday, January 4, 2013 8:35 PM
  • Your query looks fine to me (other than RessultPageSize mis-spelled). Get-ADUser already filters on user objects. I don't believe your filter can be improved. However, I find Page Size 200 best in my domain with 2300 users. I would expect Page Size 500 or even 1000 would be faster. And it might help to use Get-ADObject simply because it only retrieves 4 default properties (as opposed to Get-ADUser that retrieves 10), and add the clauses (&(objectCategory=person)(objectClass=user)) to restrict to user objects.

    The basic problems is that the AD modules are slow, several times slower that PowerShell V1 methods. Most of the time it doesn't matter, but I guess 500,000 users is too much here. I would expect the fastest method would use command line utilities (dsquery * piped to dsmod), next fastest VBScript, next PowerShell V1, and slowest the AD module cmdlets. Also, VBScript and PowerShell V1 allow you to assign a timeout. A PowerShell V1 method (partially tested) could be similar to below:

    $domain = New-Object DirectoryServices.DirectoryEntry("LDAP://ou=MyOU,dc=MyDomain,dc=com")
    $searcher = New-Object System.DirectoryServices.DirectorySearcher
    $searcher.SearchRoot = $domain
    $Searcher.PageSize = 1000
    $Searcher.SearchScope = "subtree"

    # Filter on user members of the specified group.
    $Searcher.Filter = "(&(objectCategory=person)(objectClass=user)(|(accountExpires=9223372036854775807)(accountExpires=0)))"

    # Specify attributes to retrieve.
    $Searcher.PropertiesToLoad.Add("distinguishedName") > $Null

    $Results = $Searcher.FindAll()

    $NewDate = [DateTime]"January 1, 2014"

    ForEach($Result in $Results)
    {

        $DN = $Result.Properties.Item("distinguishedName")
        $User = [ADSI]"LDAP://$DN"
        $UserType = $User.GetType()
        $UserType.InvokeMember("AccountExpirationDate", "InvokeMethod", $Null, $User, $NewDate)

    }

    -----

    To specify a timeout, I believe you must use ADO, which might be a bit slower.


    Richard Mueller - MVP Directory Services


    Friday, January 4, 2013 11:15 PM
  • The basic problems is that the AD modules are slow, several times slower that PowerShell V1 methods. Most of the time it doesn't matter, but I guess 500,000 users is too much here. I would expect the fastest method would use command line utilities (dsquery * piped to dsmod), next fastest VBScript, next PowerShell V1, and slowest the AD module cmdlets. Also, VBScript and PowerShell V1 allow you to assign a timeout.

    Thank you Richard for your helpful reply.

    I use your method (SDS) in most of my more complicated scripts.  In this case I was being lazy and tried to do the job with a one-liner, and hit the wall :(

    A few people mentioned the relative slowness of the AD Module.  Is there any study/research done on the performance of all AD interfaces?  I really like the simplicity provided by the AD Module.  However, I have, for more times than I like, run into performance related issues with it.

    Oh btw your code is incomplete.  It was cutoff inside the loop.


    Monday, January 7, 2013 2:03 AM