Finding the latest logon time

How do you find out when was the last time a particular user logged on?

(Get-QADUser username).lastLogon looks like an obvious answer but there are a few gotchas to be aware of.

The main of them: lastLogon attribute is actually not replicated between domain controllers so if you have more than one DC (which I am sure you do) you need to get it from all of them and get the latest of them.

Here’s the PowerShell code which does that:

Get-QADComputer -ComputerRole DomainController | foreach {
(
Get-QADUser -Service $_.Name -SamAccountName username).LastLogon.Value
} | Measure-Latest

Basically, we are getting a list of all DCs in the company, then prompting each of them for the user’s lastLogon time, and then picking the latest of the values (I am using my Measure-Latest function – just copy/paste if before executing this command or put in your script.)

Note that there are utilities which can do that querying and comparison for you. NetWrix guys even have a PowerShell cmdlet described here, so you can do something like:

Get-NCInactiveUsers -domain example.com -days 15

You should also keep in mind that if your users do not log off and simply lock their workstations they do not log on either – Kuma is describing here how he has a script logging off users every night to avoid this.

Another alternative is using lastLogonTimeStamp attribute instead. This one does indeed get replicated. It was introduced in Windows 2003 (make sure your schema is 2003-level or later). But keep in mind that this one is not real-time as it is only replicated every 9-14 days.

So as long as you are looking for users who have not logged on for something bigger than 2 weeks you should be good using Shay’s script for locating inactive users:

$now=get-date
$daysSinceLastLogon=60

Get-QADUser -sizeLimit 0 | where {
$_.lastlogontimestamp.value -and (($now-$_.lastlogontimestamp.value).days -gt $daysSinceLastLogon)
}
| Format-Table Name, LastLogonTimeStamp

Tags: , , , , ,

21 Responses to “Finding the latest logon time”


  1. 1 Christopher DeRemer July 18, 2008 at 2:00 pm

    When running the cmdlet for the last logon, i just get a blank prompt…

    I pasted the measure-latest and then the code you have above (removed line breaks), and saved it as a ps1 file to run. Anything extra i need to do. (Yes i’m using the ActiveRoles mgmt shell)

    Sorry to sound a little beginner, i usually don’t have any trouble with using scripts like this, but for some reason this one is just not working for me. Anything with the extended attributes of the QADUsers just gives me trouble. Any tips?!

    Cheers,
    Chris

  2. 2 dmitrysotnikov July 18, 2008 at 2:28 pm

    Chris,

    The easiest option might be to just copy/paste the function into your PowerShell profile: http://dmitrysotnikov.wordpress.com/2008/01/25/dude-wheres-my-powershell-profile/

    This would make it available in all your PowerShell sessions.

    If you want to keep it in a separate ps1 file, then you need to load (dot-source: put dot, then space, then full path to the ps1 file) the file in each PowerShell session before you use it, e.g.:

    . c:\scripts\measure-latest.ps1
    Get-QADComputer -ComputerRole DomainController | foreach {
    (Get-QADUser -Service $_.Name -SamAccountName username).LastLogon.Value
    } | Measure-Latest

    Dmitry

  3. 3 Jonathan Medd July 21, 2008 at 3:57 pm

    Hi,

    Great stuff, that is really useful for me!

    By the way, in your code towards the end:

    $now=get-date
    $daysSinceLastLogon=60

    Get-QADUser -sizeLimit 0 | where {
    $_.lastlogontimestamp.value -and (($now-$_.lastlogontimestamp.value).days -gt $daysSinceLastLogon)
    } Format-Table Name, LastLogonTimeStamp

    it’s missing a pipeline before format-table. Just a minor thing, but thought I should point it out in case anyone new to Powershell wanted to use the code.

    Thanks!
    Jonathan

  4. 4 dmitrysotnikov July 21, 2008 at 4:49 pm

    Jonathan,

    Good catch! I’ve updated the script. I have no idea how I lost the pipeline symbol, but it is back now.

    Thanks a lot for reporting that!

    Dmitry

  5. 5 Brant December 18, 2008 at 5:07 pm

    What does it mean when I run this script and the values it returns for users when it comes to last logon times are hours in the future. I can run get date in my powershell window and it returns the correct time and date but it shows some users that supposedly logged on 4-5 hours from now.

  6. 6 Brant December 18, 2008 at 6:44 pm

    Disregard my last post. As you can tell from that post there is obviously something wrong with my PC as I actually posted it at 11:07 a.m.

  7. 7 Dmitry Sotnikov December 18, 2008 at 8:59 pm

    My guess is that the time is reported in GMT so it appears to be ahead of what you have in your timezone.

  8. 8 Eero January 20, 2009 at 7:57 am

    How can i get a month history instead of just some last logons for one certain user?

    Get-QADComputer -ComputerRole DomainController | foreach {
    (Get-QADUser -Service $_.Name -SamAccountName username).LastLogon.Value
    }

    Can i specify one month here somewhere instead of LastLogon?

  9. 9 Dmitry Sotnikov January 20, 2009 at 12:24 pm

    Eero,

    Unfortunately Active Directory does not store this information and only keeps the last logon date.

    Depending on how granular you need to get (and your budget ;) ) you could either set your script to pull the data from DCs on some kind of schedule (e.g. daily – obviously you will lose some data in that case if users log on more often than that) or use a commercial AD auditing solution such as http://www.quest.com/changeauditor-for-active-directory/

    Dmitry

  10. 10 Tony January 29, 2009 at 4:26 pm

    Dmitry, I have found this script to be unreliable. I can compare the results with a commercial application and it finds other inactive users which are enabled and have a lastlogontimestamp value older than 60 days. I was told there is a bug in get-qaduser cmdlet, would you happen to know a reliable workaround to finding inactive users?

    Thanks!

  11. 11 Dmitry Sotnikov January 29, 2009 at 4:58 pm

    Tony,

    What I would do is try to troubleshoot these few accounts which don’t get included – there should be something different in them. Let’s use the AD PowerShell forum at PowerGUI.org to do this.

    Dmitry

  12. 12 Tony January 29, 2009 at 5:09 pm

    Cool Dmitry, I already started one under RequestScript! Cya there!

  13. 14 Rafael Corvalan January 30, 2009 at 5:50 pm

    Dmitry,

    May a suggest a more efficient way for the second script? In fact, you fetch all users from ActiveDirectoy (huge!) and then you filter.

    I think it’s better to let ActiveDirectoy to filter by its own doing something such as:

    1# $daysSinceLastLogon=60
    2# $d=(get-date).AddDays(-$daysSinceLastLogon).ToFiletimeUTC()
    3# Get-QADUser -sizelimit 0 -ldapFilter “(lastLogonTimeStamp<=$d)"

    The only difference should be borderline users since sour script rounds the dates difference (days method returns an integer).

    Now, let’s look at performance:

    1# (Measure-Command {$now=get-date; $daysSinceLastLogon=60; Get-QADUser -sizeLimit 0 | where {$_.lastlogontimestamp.value -and (($now-$_.lastlogontimestamp.value).days -gt $daysSinceLastLogon)}}).TotalSeconds
    72.3227027
    2# (Measure-Command {$daysSinceLastLogon=60; $d=(get-date).AddDays(-$daysSinceLastLogon).ToFiletimeUTC(); Get-QADUser -sizelimit 0 -ldapFilter "(lastLogonTimeStamp<=$d)"}).TotalSeconds
    5.8164012

    Not bad, isn’t it?

    Regards,

    Rafael

  14. 15 Dmitry Sotnikov January 30, 2009 at 9:20 pm

    Rafael,

    Yes, using ldapfilter and thus filtering on the server site is a MUCH better way to go. Thanks for the excellent point.

    Dmitry

  15. 16 seaJhawk March 4, 2009 at 11:19 am

    Thanks guys – this is a very helpful post.

    In my case, we expire accounts after 180 days so I need to show an ExpirationDate for each account so the person who is reviewing the accounts knows which ones are going to expire soon. Here’s how I did it:

    # return accounts in the same OU as the user that will expire in 7 days
    $maxAccountAgeDays = 180
    $daysSinceLastLogon = $maxAccountAgeDays – 7
    $d=(get-date).AddDays(-$daysSinceLastLogon).ToFiletimeUTC()
    $ldapFilter = “(lastLogonTimeStamp<=$d)”
    $searchRoot = (get-qadUser $env:UserName).ParentContainerDN

    # Get the users
    Get-qadUser -ldapFilter $ldapFilter -SizeLimit 0 -SearchScope Subtree -SearchRoot $searchRoot -ErrorAction SilentlyContinue |
    Add-Member -MemberType ScriptProperty -Name ExpirationDate -PassThru -Value {
    [datetime]::Parse($this.LastLogonTimeStamp).AddDays(180).ToLongDateString()
    } | Format-Table Name, LastLogonTimeStamp, ExpirationDate

  16. 17 jimhowe March 5, 2009 at 3:42 pm

    Wow, I posting my questions here very poorly. Now that I am at the right post:

    How can you feed the “Measure-Latest” value of the latest logon query into a csv with the rest of other say, get-qadgroupmember information (ie, members of group X logonname, pwdlastset, ‘latest logon value’)?
    Thanks!

  17. 18 Mike April 8, 2009 at 9:29 am

    Hi – thats great that we can get a users last logon, but can you do the same to get a computer last logon to the domain keeping in mind we have more than one DC so it would have to query them to, thanks in advance

  18. 20 Dmitry Sotnikov April 8, 2009 at 4:20 pm

    Jim,

    Please use the AD PowerShell forums for questions like that: http://powergui.org/forum.jspa?forumID=173

    Of the top of my head this is what you would probably want to do:

    Get-QADGroupMember -Name mygroup | foreach {

    $user = $_

    Get-QADComputer -ComputerRole DomainController | foreach {

    $latestlogon = (Get-QADUser -Service $_.Name -SamAccountName $user.SamAccountName).LastLogon.Value
    } | Measure-Latest

    $user | Add-Member -MemberType NoteProperty -Name ‘LatestLogon’ -Value $latestlogon -PassThru

    } | Export-CSV

    This is going to be a pretty slow script. You can somewhat optimize it


  1. 1 Disable Old AD Accts « Kontonnz’s Blog Trackback on April 18, 2009 at 2:23 pm

Leave a Reply




View Dmitry Sotnikov's profile on LinkedIn

Follow Dmitry Sotnikov at Twitter

My Recent Tweets

Archives

See you at:

Legal

The posts on this blog are provided “as is” with no warranties and confer no rights. The opinions expressed on this site are mine and mine alone, and do not necessarily represent those of my employer Quest Software or anyone else for that matter. All trademarks acknowledged.

© 2007 Dmitry Sotnikov

Pages

 

July 2008
M T W T F S S
« Jun   Aug »
 123456
78910111213
14151617181920
21222324252627
28293031