Friday, January 7, 2011

Active Directory Epoch vs. PowerShell Epoch in Attribute Based LDAP Queries

Active Directory stores time as the number of 100-nanosecond intervals (ticks) that have elapsed since midnight, January 1, 1601 UTC (GMT) in attributes such as LastLogon, LastLogonTimestamp, LastPwdSet and AccountExpires. PowerShell's epoch begins midnight, January 1, 0001 UTC. This leads to a little problem when you want to search objects by time-based attributes via an LDAP query in Active Directory using PowerShell. You can't simply say "(AccountExpires<=" + ((Get-Date).AddDays(-90)).Ticks + ")" to search for user objects that expired 90 days ago. You have to calculate the offset between Active Directory epoch and PowerShell epoch. To do this, you need to store the Active Directory epoch as  DateTime in PowerShell and subract its Ticks from the DateTime Ticks you are interested in querying against. Commonly, you will find scripts on the Internet that do the same process but have you return all objects and loop them through an if/then statement to perform the same process on a calculated attribute. Doing this ticks-based query upfront is more efficient and returns results much faster -- particularly in domains with large numbers of objects. Here is an example of an LDAP filter that could be used to find user objects that have expired 90 or more days ago.
$activeDirectoryEpoch = (Get-Date "1601-1-1T00:00:00-00:00").ToUniversalTime()
$expirationDate = (((Get-Date).AddDays(-90)).ToUniversalTime()).Ticks - $activeDirectoryEpoch.ticks
$ldapFilter = "(&(objectCategory=person)(objectClass=user)(AccountExpires<=$expirationDate)(!AccountExpires=9223372036854775807)(!AccountExpires=0))"

No comments:

Post a Comment