Get-ADUser over multiple OUs and filter results
There are a few recommendations I'd make with your current script.
First off, a single large query is almost always going to perform better than many smaller queries. So rather than running get-aduser
separately for each target OU, I would combine them into a single call using a higher level common OU as the search base. Obviously, this may end up returning results from OUs you didn't want to include. But it's much faster to filter those out later.
You're also calling get-aduser
again for each result from the first set of queries just to filter on lastLogonDate. But you could instead combine that filter with the -ldapfilter
from your original queries. It's just a matter of converting the -filter
version with an equivalent -ldapfilter
version. The secret to doing this is knowing that lastLogonDate is just a Powershell converted version of the lastLogonTimestamp attribute. And you can convert a normal Powershell DateTime value into the format that lastLogonTimestamp uses with the ToFileTime()
method.
The last thing that confused me was the (UserPrincipalName=*)
portion of your ldapfilter. In every domain I've ever touched, this attribute will always have a value (just like SamAccountName or DistinguishedName). It may be different than the default value of <SamAccoutnName>@<DomainFQDN>
, but it's never empty. The filter isn't hurting anything necessarily. It's just one extra thing for AD to spend CPU cycles evaluating when it doesn't need to. But if you have reason to believe it might be empty in your environment, by all means leave it in.
So here's how I'd modify your script if I understand your intentions correctly.
# make the comparison value using ToFileTime()
$30daysago = (Get-Date).AddDays(-30).ToFileTime()
# make the combined ldapfilter value
$LdapFilter = "(&(lastLogonTimestamp<=$30daysago)(extensionAttribute9=*)"
# make an array of the OU DNs you care about
$TargetOUs = @(
"OU=Users,OU=US-Location,OU=Americas,DC=Domain,DC=Domain,DC=com"
"OU=Users-Remote,OU=US-Location,OU=Americas,DC=Domain,DC=Domain,DC=com"
"OU=Contractors,OU=US-Location,OU=Americas,DC=Domain,DC=Domain,DC=com"
"OU=Temps,OU=US-Location,OU=Americas,DC=Domain,DC=Domain,DC=com"
)
# define your common search base
$base = "OU=US-Location,OU=Americas,DC=Domain,DC=Domain,DC=com"
# get your combined results and the additional attributes you care about
$OldUsers = get-aduser -ldapfilter $LdapFilter -searchbase $base -pr lastLogonDate
# convert the target OU list into a regular expression we can compare each DN against in a single comparison call
$regex = ""
$TargetOUs | %{ $regex += ".*," + [Regex]::Escape($_) + "$|" }
$regex = $regex.Substring(0,$regex.Length-1)
# filter the results that aren't in your target OUs
# (depending on your OU layout, it might be more efficient to flip this
# and define the OUs you explicitly want to leave out)
$FilteredUsers = $OldUsers | ?{ $_.DistinguishedName -match $regex }
# export your CSV (sorted for good measure)
$FilteredUsers | select SamAccountName,LastLogonDate | sort LastLogonDate | export-csv C:/Users/myname/Desktop/Usersover30days.csv
P.S. Be wary of treating lastLogonTimestamp
(or lastLogonDate
) as 100% accurate. It may be anywhere from 9 to 14 days out of date by design.