Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Parallelise Active Directory queries

I want to get all the lastlogon dates from all Active Directory users of all domain controller. This will result in a list with all users and their latest logon date. My script is working fine, but it is possible to parallelise it even more?

At the moment it takes around 1 second to query all 20 domain controller for only one user (Invoke-Parallel is helping a lot).

The problem here is we have ~120k user

Here is the code:

. 'D:\scripts\InPa\Invoke-Parallel-lite-nolog.ps1'

function Get-UsersLastLogon{
    $AllDCs = ([System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain().domainControllers).Name
    $AllUser = ((New-Object System.DirectoryServices.DirectorySearcher("(&(objectCategory=User))")).FindAll().Properties).samaccountname

    foreach($SamAccountName in $AllUser){
        $Latest = $AllDCs | Invoke-Parallel -Throttle 60 -ScriptBlock {
        $de=[adsi]"LDAP://$_"
        $UserSeacher = (New-Object System.DirectoryServices.DirectorySearcher($de,"(&(objectCategory=User)(samaccountname=$Using:SamAccountName))")).FindAll().Properties

        $co = New-Object System.Object
        $co | Add-Member -type NoteProperty -name SamAccountName -value $Using:SamAccountName
        $co | Add-Member -type NoteProperty -name lastlogon -value ([datetime]::FromFileTime([string]$UserSeacher.lastlogon))
        $co

        }
        $Latest | Sort-Object -Property lastlogon -Descending | Select-Object -First 1
    }
}

Get-UsersLastLogon | Export-Csv -Path D:\UsersLastLogon.csv -Delimiter ';' -NoTypeInformation

Do you have an idea to speed this up even more?

like image 693
psott Avatar asked Aug 31 '25 23:08

psott


1 Answers

Assuming you have 20 Domain Controllers, you're currently doing 20 * 120000 + 2 LDAP queries - that's almost 2 and a half million queries.

What you can do instead is query each DC once, and retrieve all users in one go per DC:

function Get-UsersLastLogon{

    # Gather all DC names
    $AllDCs = ([System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain().domainControllers).Name

    # In parallel, query the DCs for ALL users
    $AllEntries = $AllDCs | Invoke-Parallel -Throttle 60 -ScriptBlock {
        $de=[adsi]"LDAP://$_"
        $UserSeacher = New-Object System.DirectoryServices.DirectorySearcher($de,"(&(objectCategory=User))")
        [void]$UserSeacher.PropertiesToLoad.Add('lastLogon')
        $Users = $UserSeacher.FindAll().Properties

        foreach($User in $Users){
            New-Object psobject -Property @{
                SamAccountName = $User.SamAccountName
                LastLogon = ([datetime]::FromFileTime([string]$User.lastlogon))
            }
        }
    }

    # Group all the results by user name
    foreach($UserEntry in $AllEntries |Group-Object SamAccountName){
        # Emit the newest entry per username
        $UserEntry.Group |Sort-Object -Property LastLogon -Descending | Select-Object -First 1
    }
}
like image 134
Mathias R. Jessen Avatar answered Sep 03 '25 13:09

Mathias R. Jessen