ADUser Script help required

Hi all,

I have a PS script I have wanted to use for some time. This is to retrieve a list of users with their most up-to-date lastlogon attribute against their object. This is the attribute that does not replicate across the domain, so I need to pull data of users from each DC and compare records. “Why don’t you use lastlogontimestamp?” I hear you ask, well - because I don’t want to. Note, I did get some of this script from a site somewhere but I do not remember where.

Essentially it works, in so much as it gives me an output of users and the converted timestamp of their last logon. Only, it is working the wrong way around and giving me their furthest lastlogon, which I am guessing is against a DC they have never authenticated against since its listed as 1st January 1601. Any help resolving this would be greatly appreciated. Will accept ridicule if I have missed something obvious:

$DCs = Get-ADDomainController -Filter * | Select -ExpandProperty Name
$Users = Foreach ($DC in $DCs)
    Get-ADUser -Filter * -Searchbase "OU=Users,OU=Company,DC=noob,DC=com" -Searchscope Subtree -Server $DC

$logins = @{}

foreach ($User in $AllUsers)
    If ($logins.ContainsKey($User.SamAccountName))
        { If ($logins[$User.SamAccountName].lastLogon -gt $User.lastLogon)
            $logins[$User.SamAccountName] = $Logins[$user.SamAccountName]
        $logins.Add($User.SamAccountName,($User | Select SamAccountName,Name,lastLogon))

$Report = $logins.values | Select Name,SamAccountName,@{Name = 'Last Logon'; Expression={[datetime]::FromFileTime($_.lastLogon)}}
$Report | Format-Table

“I want the car to go, but I don’t want to use the gas pedal, because I just don’t want to.” [grin] LastLogonTimestamp was literally made for this exact reason and using it represents a far lower burden on the AD infrastructure. True, it replicates at low priority, but run your report late at night and it’ll have had time to coalesce.

Still, let’s just take it as an intellectual exercise.

And yikes, that’s a LOT of data to query from a lot of places and then manipulate in RAM. Still - intellectual exercise!

Date comparisons in PowerShell only work correctly on the [DateTime] or other date/time-related types. I don’t -see- where you’re creating a proper datetime object for comparison, so it’s possible that’s it. The AD commands aren’t 100% awesome at returning the right object type - you get [string] a lot - so it’d be worth looking into that.

foreach ($User in $AllUsers) - where did $AllUsers get defined? You queried users into $Users.

Hi Don,

Thanks for your reply.

Sorry! Ignore $AllUsers. I was playing around and left a sketchy variable.

The idea here was just to look at the lastlogon attribute as it appears as Filetime and retrieve the higher number against the relevant AD object. I converted it later when defining $report.

Having looked into the differences between lastlogon and lastlogontimestamp I was led to believe that there could be up to days of discrepancy for a users actual “last logon”. Requirement was specifically for the last 30 days of login - hence the plan to use lastlogon (I am not really just being difficult for difficult sake :P).

If lastlogontimestamp is not so out then I am happy to use that (I have been so far since I could not get this to work).

It replicates on low priority, but “days” is an exaggeration in my experience. Do some testing - easy enough - and see if it’s happening quickly enough in your environment.

For PowerShell, a filename is really just a string, and so comparisons will be based on the ASCII values of the digits. That might not be what you want, which means you’d have to convert it to a [datetime] for -gt or -lt to “see” the value correctly.

I guess one of the issues is that the replication of lastlogontimestamp can take between 9-14 days, by default.
So it depends on the reason for the report I guess.

On the other hand depending on the environment, looking through lastlogon for every DC and user might be a bit excessive.


  1. (Assuming the value of the ms-DS-Logon-Time-Sync-Interval is at the default of 14)

  2. User logs on to the domain

  3. The lastLogontimeStamp attribute value of the user is retrieved

  4. 14 – (Random percentage of 5) = X

  5. Current date – value of lastLogontimeStamp = Y

  6. X ≤ Y – update lastLognTimeStamp

  7. X > Y – do not update lastLogontimeStamp

If it’s within a 30 day period then lastlogontimestamp should be well within the default settings.

Hi Both,

Thanks for replies.

Looks like using lastlogontimestamp is just the way to go moving forward then, thanks for your input.

I would be interested to see if I can get this working by converting Filetime to datetime before comparing the objects, I may just fiddle about with it in a test/dev environment for funsies.

Thanks again.

The main problem with lastlogontimestamp is that it can be updated by things other than interactive logons by the user. If you are ok with that, then by all means use it as a shortcut. If you are writing code to enforce deactivating accounts not used for x days, you can’t use it. I work in an environment where I deal with multiple customers, and explaining the problem with lastlogontimestamp is always hard. But I’ve seen accounts where the user has been gone for years with a recent lastlogontimestamp, and chasing down the DCs shows that the account was not actually used. If you dig around deep enough, there’s a vague explanation from Microsoft about binding or some such.