powershellactive-directorystart-job

Powershell | Speed up Running Get-ADUser in Powershell jobs


I'm working on a tool that can quickly run through a csv and search AD for the relevant information provided I have made a gui that will let the user align the header of a CSV with the AD search method

My main issue at the moment is when searching AD for EmailAddress variable i am often getting the error "A connection to the directory on which to process the request was unavailable. This is likely a transient condition."

and its erroring out on select entries Limiting the number of powershell jobs running at any one time appears to assist with the issue but does not eliminate it entirely

Here is what I'm working with at the moment

$maxConcurrentJobs=15
$CheckBlock = {
            param ($User)
            Try { Get-ADUser -Filter { EmailAddress -eq $User } -Properties SamAccountName, EmployeeID, EmailAddress }
            Catch { Return "$User - $_" }
        }
        $Array.($listbox_Columns.SelectedItem) | ForEach-Object{
            $Check = $false 
            while ($Check -eq $false)
            {
                if ((Get-Job -State 'Running').Count -lt $maxConcurrentJobs)
                {
                    Write-Host "Processing EmailAddress $_"
                    Start-Job -ScriptBlock $CheckBlock -ArgumentList $_
                    $Check = $true
                }
            }
        }

Solution

  • I'd suggest moving to -LDAPFilter, like this:

    Get-ADUser -LDAPFilter "(mail=$User)" -Properties SamAccountName, EmployeeID, EmailAddress
    

    And building from that, the optimum would be a single search that gets all users in one go. This can also be done with -LDAPFilter, but it requires a bit more doing.

    $mails = $listbox_Columns.SelectedItem  # this should be an array of email addresses
    $filter = $mails -join ')(mail='
    
    Get-ADUser -LDAPFilter "(|(mail=$filter))" -Properties SamAccountName, EmployeeID, EmailAddress
    

    Chances are high that you don't need to distribute this across many jobs anymore at this point, this is as efficient as it gets.

    What's happening in the second code sample:

    $mails -join ')(mail=' together with (|(mail=$filter)) creates an LDAP search expression in the form of (|(mail=A)(mail=B)(mail=C)) and so on, which would give you all the matching objects (but nothing else) in one server round-trip.

    Of course you need to familiarize yourself with the LDAP search filter syntax and take a look at the raw LDAP property values in your AD to use it effectively, but this is a small price to pay for the performance gains it provides.