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
} | 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 -and 
    (($now-$_.lastlogontimestamp).days -gt $daysSinceLastLogon)
} | Format-Table Name, LastLogonTimeStamp

Finally, you can speed things up considerably by constructing an LDAP query and thus doing all the filtering on the server side:

# calculate a deadline date. (now minus 60 days)
$deadline = (Get-Date).AddDays(-60).ToFileTimeUtc()

#construct a ldap query
$ldapQuery = '(|(!(lastLogonTimeStamp=*))(lastLogonTimeStamp<=' + $deadline + '))'

#run this query
Get-QADUser -Enabled -SizeLimit 0 -LdapFilter $ldapQuery

Tags: , , , , ,

About these ads

39 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. 5 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. 6 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. 7 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. 8 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. 9 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. 10 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. 11 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. 12 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. 13 Tony January 29, 2009 at 5:09 pm

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

  13. 15 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. 16 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. 17 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. 18 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. 19 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. 21 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

  19. 22 clubidentityplatform December 8, 2009 at 11:58 pm

    We have a few weeks off for the holiday and I would like to get a list of user accounts with passwords that will expire over the holiday so I can proactively warn them now.

  20. 23 Dmitry Sotnikov December 14, 2009 at 9:32 am

    clubidentityplatform,

    Could you post this question to the Active Directory and PowerShell forum at http://powergui.org/forum.jspa?forumID=173

    Dmitry

  21. 24 roman September 10, 2010 at 11:55 pm

    For some reason, the first script does not work for me. Maybe it’s now different because I have newer versions of the scripting software. Anyway, for me it works if I leave out the .Value.

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

  22. 26 JokerHead October 8, 2010 at 12:02 am

    We have too many DC, so I use:

    Get-QADComputer -ComputerRole DomainController | Where-Object {$_.Name -Match “MY_DC”} | Foreach-Object{
    $dc = $_.Name
    Get-QADUser -Service $dc -SamAccountName $SamAccountName
    } | Select-Object Name,Lastlogon

    • 27 Dmitry Sotnikov October 8, 2010 at 5:00 pm

      So you have a bunch of DCs in your site, they all start with MY_DC and you want to search across all of them?

      In that case this would work faster than Where-Object:

      Get-QADComputer -ComputerRole DomainController -Name MY_DC*

  23. 28 Jesse February 11, 2011 at 3:09 pm

    Maybe you can help me out.. I’m using the Locateoldusersnotlogon.ps1 file in the Win2008 Resource kit. Our domain is huge and would like to isolate the search to a specific OU. How would I modify the script to have it search a specific OU?

  24. 30 Stokbroka April 15, 2011 at 4:42 am

    I am curious how you work out the actual days since the last logon. I dont want the lastlogon time (All DC’s Windows 2000) I actually want the amount of days listed. I cannot work out how to perform this calculation.

    Thanks

    • 31 Dmitry Sotnikov April 15, 2011 at 7:19 pm

      Are you asking on how to get the number of days between 2 dates? Simply subtract one from another:

      $a = get-date
      $b = get-date ’01/01/2011′
      $a – $b

  25. 33 Dmitry Sotnikov April 18, 2011 at 11:59 pm

    Alan,

    PowerShell parser thinks that the minus is a parameter for your get-date cmdlet and goes crazy. Just use brackets to make it easier for the parser:

    (get-date) – ($_.lastlogon)

    Dmitry

  26. 34 Alan April 19, 2011 at 4:54 am

    Cheers Dmitry, thats got it.

  27. 35 Jaime October 11, 2011 at 2:32 pm

    Dmitry,

    I’ve been trying to run the script:
    Get-QADComputer -ComputerRole DomainController | foreach {
    (Get-QADUser -Service $_.Name -SamAccountName username).LastLogon
    } | Measure-Latest
    Using the ActiveRoles Management Shell, and have not been able to obtain results, first of all, Measure-Latest have no idea how to run it, I followed your instructions and still I am unable to do this I downloaded the PowerGUI and still. The last attempt that I ran was this:
    Get-QADComputer -ComputerRole DomainController | foreach {
    (Get-QADUser -Service $_.Name -SamAccountName username).LastLogon
    } | Format-Table and I did not see any resultst displaying on the screen.

    Any Ideas?

  28. 36 Mike Wilson August 22, 2012 at 8:42 pm

    I’m looking to read in a list of users from a csv and then search for their lastlogin time. How would I do that with the above ? I feel like i am lost in foreach loops

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


  1. 1 Disable Old AD Accts « Kontonnz’s Blog Trackback on April 18, 2009 at 2:23 pm
  2. 2 Friday Mail Sack: It’s a Dog’s Life Edition - Ask the Directory Services Team - Site Home - TechNet Blogs Trackback on January 21, 2012 at 4:13 am
  3. 3 Friday Mail Sack: It’s a Dog’s Life Edition Trackback on January 21, 2012 at 10:31 am

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s




My Recent Tweets

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 - WSO2 or anyone else for that matter. All trademarks acknowledged.

© 2007-2014 Dmitry Sotnikov

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

Follow

Get every new post delivered to your Inbox.

Join 2,326 other followers

%d bloggers like this: