In “Use Get-ADUser to Determine Who Has Never Logged On” (InstantDoc ID 141189), I showed you how to use Active Directory's (AD’s) PowerShell tools to find never-used user accounts. At the same time, I revealed the AD attributes accessible from PowerShell that might give you an idea about who's logged on and—more important—who hasn't logged on in a while. This month, let's search for people who haven't logged on to a domain in, say, the past 180 days.
Windows and AD retain a number of when-you-last-logged-on-related attributes, but they're all a pain to get to. You could look at account-logon events, but you'd have to grab them from every domain controller (DC) and sort them by date and person to find the most recent of those events for each person. Not fun! Alternatively, you could grab something like AD's lastlogon user attribute, but again DCs don't replicate that value. So, as in the case of the account-logon event, you'd have to retrieve lastlogon's value from every DC in the domain, then find the most recent one for each use. That’s too much work.
Fortunately, on Windows Server 2003 and later domain functional levels, Microsoft added lastlogontimestamp, an AD attribute that is replicated amongst DCs. It's stored as a fairly ugly large integer (as I write this in October 2011, lastlogontimestamp's value is 129623232699932000) that isn't very easy to work with. Thus, it's a blessing that the AD PowerShell folks convert that into a straightforward value called LastLogonDate that looks like Saturday, October 08, 2011 1:07:18 PM, accessible via Get-ADUser. Knowing this, you can use this command to create a table of users sorted by when they last logged on:
get-aduser -f * -pr lastlogondate|sort -property lastlogondate|ft samaccountname,lastlogondate -auto
Thus, you can query any DC and see whether any user last logged on to the domain around, say, September 22, 2011. Well, sort of. You see, Microsoft added lastlogontimestamp to AD because many AD administrators wanted some way to track logons, but Redmond worried that adding something to AD that closely tracked logons could significantly increase AD's replication burden. To reduce lastlogontimestamp-related replication traffic, DCs update the value only every 9 to 14 days. Whenever a DC logs you on, that DC looks at your current lastlogontimestamp value. The DC then picks a random real number between 9 and 14. If the number of days between when you last logged on and now is smaller than the random number, the DC doesn’t update your lastlogontimestamp value. As a result, each user's lastlogontimestamp value gets updated only once every 12 days or so—no matter how often that user logs on during that time period. Less precision, yes, but also a lot less replication traffic.
Suppose I log on to my domain at noon on January 20, 2012, and I haven't logged on since 9:00am, January 9 of that year. Thus, I haven’t logged on in 11.25 days. My DC then chooses a random number between 9 and 14. Let’s assume it chooses 13.44. The chosen random number (13.44) is larger than the time span between my last two logons (11.25), so my DC doesn't change my lastlogontimestamp value upon this logon. Clearly, then, although lastlogontimestamp is useful, it might be wrong by as many as 14 days, so it's of no use to identify who hasn't logged on in the past two weeks. For finding those who haven’t logged on in about six months, though, it's quite useful.
You could try to do some gymnastics with Get-ADUser to find those users who haven't logged on in the past 180 days, but the AD PowerShell folks saved us the effort with a cmdlet called search-adaccount, which lets you perform this query:
search-adaccount -accountinactive -usersonly -timespan "195"
That looks simple, but there are two things about this command that are unintuitive. First, notice the double quotes around the number 195: The -timespan parameter absolutely requires those. Second, notice that it's 195, not 180. (And remember, you don't get lastlogontimestamp unless you're in Windows 2003 domain functional level or greater.) Search-adaccount has a quirk that requires you to add 15 to your target period of inactivity, and in truth the value isn't exactly 15 days. To learn why it does that and how to figure out the exact value to enter—as well as an alternative syntax to find inactive users—please join me next month.