Finding Stale Accounts in AD with Windows PowerShell
Posted by Ben Lye in Active Directory, PowerShell, Windows PowerShell on 16-02-2010
Tags: Active Directory, PowerShell, Windows PowerShell
In an Active Directory domain running at the Windows Server 2003 or higher functional level the lastLogonTimestamp attribute can be used to find out if a user or computer has logged on to the domain recently. This can be useful information for finding inactive user and computer accounts so that they can be removed from AD.
The lastLogonTimestamp attribute is replicated across all DCs, and it’s important to understand that it does not contain the exact time and date at which the account last logged on. Instead there is an algorithm which is used to determine when the attribute should be updated, which by default means that the timestamp in lastLogonTimestamp could be up to 14 days old.
To understand the algorithm you need to know that there is a configurable attribute in AD called msDS-LogonTimeSyncInterval, which controls the granularity of updates to lastLogonTimestamp. By default it is not set, which means that the default value of 14 is used. There is also some randomization involved to ensure that simultaneous updates from many accounts don’t cause large replication loads on the DCs. The randomization factor is a random percentage of 5 days.
The algorithm means that when a user or computer logs on the lastLogonTimestamp is only updated if the current value of lastlogonTimestamp is older than ([current date] – [14 days] + [random percentage of 5 days]). This means that the value of lastLogonTimestamp for an active account could be anything from 9-14 days old.
The last thing to know about lastLogonTimestamp is that the value is stored in UTC format as a large integer, so needs some manipulation to get into human-readable form.
Now that we understand how the attribute works we can use it to do something useful, like find all enabled computer accounts in the domain which have not logged on for 60 or more days. PowerShell is particularly suited to this because it has built-in methods for doing the date conversions.
# Calculate the UTC time 60 days ago, in FileTime (Integer) format and convert it to a string
$LLTSlimit = (Get-Date).AddDays(-60).ToFileTimeUTC().ToString()
# Create the LDAP filter for the AD query
# Searching for enabled computer accounts which have lastLogonTimestamp older than 60 days
$LDAPFilter = "(&(objectCategory=Computer)(lastlogontimestamp<=$LLTSlimit) (!(userAccountControl:1.2.840.113556.1.4.803:=2)))"
# Create an ADSI Searcher to query AD
$Searcher = new-object DirectoryServices.DirectorySearcher([ADSI]"")
$Searcher.filter = $LDAPFilter
# Execute the query
$Accounts = $Searcher.FindAll()
# Process the results
If ($Accounts.Count –gt 0) {
# Create an array to store all the results
$Results = @()
# Loop through each account
ForEach ($Account in $Accounts) {
# Create an object to store this account in
$Result = "" | Select-Object Name,ADSPath,lastLogonTimestamp
# Add the name to the object as a string
$Result.Name = [String]$Account.Properties.name
# Add the ADSPath to the object as a string
$Result.ADSPath = [String]$Account.Properties.adspath
# Add the lastLogonTimestamp to the object as a readable date
$Result.lastLogonTimestamp = `
[DateTime]::FromFileTime([Int64]::Parse($Account.Properties.lastlogontimestamp))
# Add this object to our array
$Results = $Results + $Result
}
}
# Output the results
$Results | Format-Table -autosize
Extending this script to disable the discovered accounts is as easy as adding this code snippet to the end:
# Disable each account
ForEach ($Result in $Results) {
$ADSIAccount = [ADSI]$Result.ADSPath
$ADSIAccount.PSBase.InvokeSet("AccountDisabled", "True")
$ADSIAccount.SetInfo()
}
The lastLogonTimestamp attribute is useful for system administrators to understand, and because PowerShell can handle all the type conversions and date arithmetic, it is a great tool to use when working with it.
[...] a lot of time researching subjects for blog posts that will be helpful and interesting to you, from Finding Stale Accounts in Active Directory with Windows Powershell and Renewing an Expired certificate in Exchange 2007 to Creating a Custom RBAC Role in Exchange [...]