System.OutOfMemoryException when running Get-Aduser on large data sets

151 Views Asked by At

The below script is considerably cut down in terms of the number of user properties being requested and there are approximately 50,000 users to iterate through. My previous approach was to first get a collection of allusers then do a get-aduser on each one for the information, however that approach was taking a long time (up to 16 hours). With the approach in the script below where the get-aduser is only run once the script flies through doing approximately 20,000 users in 11 minutes. However at that stage you can suddenly see it slow down and eventually crashes out with the error pasted at the end of the code. Is there a way around this issue?

$csv = "Computers-{0:dd-MM-yyyy_HHmm}.csv" -f (get-date)
$UsrProps = "SamAccountName",
"AccountExpirationDate",
"accountExpires",
"AccountLockoutTime",
"BadLogonCount",
"badPwdCount",
"badPasswordTime",
"SamAccountName"


Get-ADUser -Filter *  -Properties $UsrProps -server $domain |
ForEach-Object {

    $hash = @{
        AccountExpirationDate = $_.AccountExpirationDate
        AccountLockoutTime    = $_.AccountLockoutTime
        accountExpires        = $_.accountExpires
        BadLogonCount         = $_.BadLogonCount
        badPwdCount           = $_.badPwdCount
        badPasswordTime       = $_.badPasswordTime
        SamAccountName        = $_.SamAccountName
    }
       
    $PSCustObj = [pscustomobject]$hash
    $results = $PSCustObj
    $results |
    select-object @{ l = "SamAccountName"; e = { [string]$_.SamAccountName } },
    @{ l = "AccountExpirationDate"; e = { [string]$_.AccountExpirationDate } },
    @{ l = "AccountLockoutTime"; e = { [string]$_.AccountLockoutTime } },
    @{ l = "BadLogonCount"; e = { [string]$_.BadLogonCount } },
    @{ l = "badPwdCount"; e = { [string]$_.badPwdCount } },
    @{ N = 'badPasswordTime'; E = { [DateTime]::FromFileTime($_.badPasswordTime) } }  | 
    export-csv "$PWD\Logs\$domain\Users\$csv" -noTypeInformation -Append
    
}


Get-ADUser : Exception of type 'System.OutOfMemoryException' was thrown. At line:231 char:5
+     Get-ADUser -Filter *  -Properties $UsrProps -server $domain |
+     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Get-ADUser], OutOfMemoryException
    + FullyQualifiedErrorId : ActiveDirectoryCmdlet:System.OutOfMemoryException,Microsoft.ActiveDirectory.Management.Commands.GetADUser
1

There are 1 best solutions below

7
mklement0 On BEST ANSWER

You can greatly streamline your command, which may fix the out-of-memory problem (it definitely reduces overall memory consumption and speeds up your command):

Get-ADUser -Filter * -Properties $UsrProps -server $domain |
  Select-Object @{ l = 'SamAccountName'; e = { [string]$_.SamAccountName } },
    @{ l = 'AccountExpirationDate'; e = { [string]$_.AccountExpirationDate } },
    @{ l = 'AccountLockoutTime'; e = { [string]$_.AccountLockoutTime } },
    @{ l = 'BadLogonCount'; e = { [string]$_.BadLogonCount } },
    @{ l = 'badPwdCount'; e = { [string]$_.badPwdCount } },
    @{ N = 'badPasswordTime'; E = { [DateTime]::FromFileTime($_.badPasswordTime) } } | 
  Export-Csv "$PWD\Logs\$domain\Users\$csv" -NoTypeInformation

Presumably, memory pressure can still build due to deferred garbage collection, so the following variation may be necessary, which runs the .NET garbage periodically, after processing every batch of N users (tweak as needed):

$i = 0
Get-ADUser -Filter * -Properties $UsrProps -server $domain |
  ForEach-Object {

    # Run the garbage collector after processing N users.
    if (++$i % 1000 -eq 0) { [GC]::Collect(); [GC]::WaitForPendingFinalizers() }

    # Construct and output the output object for this user.
    [pscustomobject] @{
      SamAccountName        = [string]$_.SamAccountName
      AccountExpirationDate = [string]$_.AccountExpirationDate
      AccountLockoutTime    = [string]$_.AccountLockoutTime
      BadLogonCount         = [string]$_.BadLogonCount
      badPwdCount           = [string]$_.badPwdCount
      badPasswordTime       = [DateTime]::FromFileTime($_.badPasswordTime)
    }

  } |
  Export-Csv "$PWD\Logs\$domain\Users\$csv" -NoTypeInformation