Getting True LastLogonDate

I don’t know if anyone else has a hybrid 365 environment, but I am faced with a frustrating dilemma. Based on a support ticket with Microsoft and my research there is no synchronization between 365 and your local active directory for lastlogondate. This means if a user authenticates to a local AD resource vs a 365 cloud resource you can/will have a lastlogondate mismatch. Why is this so concerning you may ask.

Based on security parameters I would like to disable(disuser) accounts that have not be used in a set number of days. That is no problem already have a powershell script to achieve this. What I am trying to figure out is what is the best way to compare the date in my local AD vs the 365 environment to store the true(correct) lastlogondate?

Anyone else that has any feedback it would greatly be appreciated.

EXAMPLE

365 LastLogonDate

get-mailboxstatistics -identity doe.john | select lastlogontime

LastLogonTime

9/15/2017 8:50:55 AM

AD On-Premise LastLogonDate

PS C:\scripts\ActiveScheduledScripts>  Get-ADUser -Filter * -SearchBase "cn=doe.john,ou=axb,dc=site,dc=org" -Res
ultPageSize 0 -Prop CN,lastLogonTimestamp | Select CN,@{n="lastLogonDate";e={[datetime]::FromFileTime($_.lastLogonTimest
amp)}}

lastLogonTimeStamp

8/24/2016 2:06:23 PM

Hi Andy,

I ran into this recently as well. The problem is lastLogonTimestamp is designed for auditing stale accounts, and therefore it is only accurate up to around 14 days or so. If you want to see who hasn’t logged in for a year, this is great. But if you want the literal last time someone logged into the domain, that is a little trickier.

There is another attribute called lastLogon, which is the literal last time a user logged into a specific domain controller. The problem is that this attribute is not replicated to the other domain controllers. What I did was write a script to go to each domain controller and pull that attribute, convert to datetime like you did above, and then sort by LastLogon descending and select the first 1. To do this you would run Get-AdUser with the -Server param and specify the domain controller.

Hope this helps.

Hi,

The Last Logon time stap is only saved on 1 DC not on all and its not replicated.
Here is a way to get it

Function Get-ADUserLastLogon {
	param
	(
		[Parameter(Mandatory = $true,
				   ValueFromPipeline = $true,
				   ValueFromPipelineByPropertyName = $true,
				   ValueFromRemainingArguments = $false,
				   Position = 0)]
		[ValidateNotNullOrEmpty()]
		[Alias('Identity')]
		[string[]]$Username
	)
	
	begin {
		Write-Verbose -Message "[BEGIN  ] Starting: $($MyInvocation.Mycommand)"
		
		Write-Verbose -Message "[BEGIN  ] Finding All Domain Controllers"
		$ADDomainControllers = Get-ADDomainController -Filter {
			Name -like "*"
		}
		$NumberofDCs = ($ADDomainControllers | Measure-Object).count
		Write-Verbose -Message "[BEGIN  ] Number of Domain Controllers Found: $NumberofDCs"
	} #Begin
	Process {
		$lastLogon = 0
		Foreach ($user in $Username) {
			Write-Verbose -Message "[Process] Testing Username: $user"
			foreach ($ADDomainController in $ADDomainControllers) {
				$DCHostname = $ADDomainController.HostName
				Write-Verbose -Message "[Process] Checking on Domain Controller: $DCHostname"
				try {
					$paramGetADUser = @{
						Identity = $user
						Server = $DCHostname
						ErrorAction = 'Stop'
						Properties = 'lastLogon'
					}
					
					$ADUser = Get-ADUser @paramGetADUser
				} #Try
				Catch {
					Write-Warning "Failed to Connect to Domain Controller: $DCHostname"
				}
				Write-Verbose -Message "[Process]`t Converting To DateTime"
				$ADUserlastLogon = [DateTime]::FromFileTime($ADUser.lastLogon)
				Write-Verbose -Message "[Process]`t LastLogon Date: $ADUserlastLogon"
				
				if ($ADUserLastLogon -gt $lastLogon) {
					$lastLogon = $ADUserlastLogon
				} # If Logon is GT
			} #ForEach DC
			
			Write-Verbose -Message "[Process] Returning Result"
			$props = @{
				Username = $user
				LastLogon = $lastLogon
			}
			return New-Object -TypeName System.Management.Automation.PSObject -Property $props
			
		} #Foreach User in Username
	} #Process
	End {
		Write-Verbose -Message "[End    ] Ending:   $($MyInvocation.Mycommand)"
	}
} #Function

Get-ADUserLastLogon -Username 'value1'

Hope it helps

You can try this script:

function Get-LastLogonTime 
{ 
 
    [CmdletBinding()] 
    Param( 
        [Parameter(Mandatory=$True, 
                    ValueFromPipeline=$true, 
                    ValueFromPipelineByPropertyName=$true)] 
        [String[]]$ComputerName 
    ) 
 
    BEGIN 
    { 
        Write-Host "" 
        Write-Verbose "Checking Users . . ." -Verbose 
    } 
 
    PROCESS 
    { 
        foreach ($Computer in $ComputerName) 
        { 
            if ( Test-Connection -ComputerName $computer -Count 1 -ErrorAction SilentlyContinue ) 
            { 
                Try 
                { 
                    $Process = Get-WmiObject -Class Win32_Process -filter "Name = 'explorer.exe'" -ComputerName $Computer -ErrorAction Stop -Verbose:$false 
 
                    $Prop = [ordered]@{ 
                        'ComputerName' = $Computer 
                        'Status' = 'Connected' 
                        'UserName' = $Process.GetOwner().User 
                        'LogonTime' = ($Process.ConvertToDateTime($Process.CreationDate)) 
                        'SID' = $Process.GetOwnerSid().sid 
                    } 
                } 
                Catch 
                { 
                    Write-Verbose "Couldn't get the process on the remote computer: $Computer . . ." 
                    $Prop = [ordered]@{ 
                        'ComputerName' = $Computer 
                        'Status' = 'Disconnected' 
                        'UserName' = $Null 
                        'LogonTime' = $Null 
                        'SID' = $Null 
                    } 
                } 
                Finally 
                { 
                    $Obj = New-Object -TypeName PSObject -Property $Prop 
                    $obj.psobject.typenames.insert(0,'Get.Last.Logon.Time') 
                    Write-Output $Obj 
                } 
            } 
            else 
            { 
                Write-Warning "$Computer is offline ..." 
            } 
        } 
    } 
 
    END 
    { 
    } 
}

Additionally, you can also try this automated solution which assists to get last logon report.

One more interesting factoid about LastLogonTimestamp that you may not be aware of (and that I recently discovered much to my dismay). An update to LastLogonTimestamp can be triggered by a third party process that doesn’t even possess the affected account’s credentials!

To see this in action try this: pick an account in your domain that hasn’t logged in for some time (greater than 2 weeks). Now open up the security properties on some object with an ACL (a fileshare for instance). Now run an “effective permissions” against that account. Wait a few minutes and check the LastLogonTimestamp value for that account and you’ll see that it got updated.

And there are applications out there (such as SharePoint) that regularly run the equivalent of an “effective permissions” against large sets of users.

I’ve lost a lot of faith in the validity of LastLogonTimestamp since learning this and really wish Microsoft would fix this issue.