Link to home
Start Free TrialLog in
Avatar of SingAbout Martin
SingAbout Martin

asked on

Looping through each DC for most recent LastLogon

Thanks to an expert from this site, I got the following script which is supposed to loop through each Domain Controller, obtain the LastLogon attribute and pick the most recent LastLogon value. However I have found that it does not pick the most recent value.
When I query each DC manually for the LastLogon, I see two different values and in the resulting export the oldest value is shown.
Any ideas on how to fix this?

$DataAValues = @{}
foreach($DomainController in $DomainControllers) {
    $DomainControllerHostname = $DomainController.HostName
    $dataAParams = @{
        SearchBase  = $OrganizationalUnit
        SearchScope = 'OneLevel'
        Properties  = 'samaccountname', 'LastLogon', 'whenCreated', 'distinguishedname','PasswordLastSet'
        Filter      = { LastLogon -le $Time -AND enabled -eq $true -AND whenCreated -le $Time }
        Server      = $DomainControllerHostname
    }
    Get-ADUser @dataAParams |
        Select-Object SamAccountName,
                    Name,
                    @{Name='LastLogon'; Expression={ [DateTime]::FromFileTime($_.lastLogon).ToString('yyyy-MM-dd hh:mm:ss') }},
                    WhenCreated,
                    PasswordLastSet,
                    @{Name='ReferenceDate'; Expression={ $CurrentTimeStamp }},
                    @{Name='ADAccountDaysInactive'; Expression={ ((Get-Date) - ([DateTime]::FromFileTime($_.lastLogon))).Days }},
                    distinguishedname |
        ForEach-Object {
            if ($DataAValues.Contains($_.SamAccountName)) {
                if ($DataAValues[$_.SamAccountName].LastLogon -lt $_.LastLogon) {
                    $DataAValues[$_.SamAccountName].LastLogon = $_.LastLogon
                }
            } else {
                $DataAValues.Add($_.SamAccountName, $_)
            }
        }
}
$DataA = $DataAValues.Values | Select-Object *

Open in new window

SOLUTION
Avatar of oBdA
oBdA

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of SingAbout Martin
SingAbout Martin

ASKER

Thank you oBdA for your reply, however this appears to be a little bit different from what I'm working with right now. Could you possibly motivate the reason my script is not working as expected, and how I can implement a fix in my current script? If I have to use your script I have to rebuild everything.
Hi

I have run this through once so that $dataa gets defined and then check the first 10 users using this modified version to verify the results

$DataAValues = @{}
$domainControllers = (Get-ADForest).Domains | %{ Get-ADDomainController -Filter * -Server $_ } 

foreach($DomainController in $DomainControllers) {
    $DomainControllerHostname = $DomainController.HostName
    $dataAParams = @{
        SearchBase  = $OrganizationalUnit
        SearchScope = 'OneLevel'
        Properties  = 'samaccountname', 'LastLogon', 'whenCreated', 'distinguishedname','PasswordLastSet'
        Filter      = {enabled -eq $true} #{ LastLogon -le $Time -AND enabled -eq $true -AND whenCreated -le $Time }
        Server      = $DomainControllerHostname
    }
    Get-ADUser @dataAParams | where {$_.samaccountname -eq "YouUsernametoTestHere"} |
        Select-Object SamAccountName,
                    Name,
                    @{Name='LastLogon'; Expression={ [DateTime]::FromFileTime($_.lastLogon).ToString('yyyy-MM-dd hh:mm:ss') }},
                    WhenCreated,
                    PasswordLastSet,
                    @{Name='ReferenceDate'; Expression={ $CurrentTimeStamp }},
                    @{Name='ADAccountDaysInactive'; Expression={ ((Get-Date) - ([DateTime]::FromFileTime($_.lastLogon))).Days }},
                    distinguishedname 
}

Open in new window



replacing  it with      "YouUsernametoTestHere"  with each username in $dataa  .   The script seems to to what u want and return the most recent date for each user , and i am unable to get to fail.

I hope this helps
Joe
Thank you Joe, but this is not exactly what I mean. The script you provided does return the LastLogon attribute value from all available Domain Controllers. However, it returns all time stamps. I only want to return the most recent logon time stamp. Because that is the value that I will be working with.
Hi
The modified code i provided was to assist you in validating the output from your original script.  I believe that your script is working , and your previous validation may of been the issue , and not the script.

to validate your results try the following

run your original script and store the results into a file ( $dataa | export-csv ADusers.csv -notypeinformation )
open this file  and select some random users ,
enter them one at a time into the script i supplied top verify the date in the file , is the most recent date

Please let me know if i can help further

Joe
Hi Joe,

With your script it still doesn't export the most recent value. This makes sense because I think it needs to make a calculation, which is supposedly done in the ForEach-Object section of the original script I posted (but that doesn't work properly). In your script, I don't see where it is calculating the highest value and putting that forward.
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Good one oBdA, but unfortunately, still the older value is returned.
Hi

how are you determining the older value is being returned ?

and line 22  was correct before

f ($DataAValues[$_.SamAccountName].LastLogon -lt $_.LastLogon) {

Open in new window


@obda     good catch with the HH in the date formatting.
Using this script, then after running this, querying $DataA. If I change the $DomainControllers to what you suggested, this makes no difference in the result.

$DaysInactive = 60
$Time = (Get-Date).Adddays(-($DaysInactive))
$CurrentTimeStamp = (Get-Date).ToString('yyyy-MM-dd HH:mm:ss')
$OrganizationalUnit = 'OU=Users,DC=Contoso,DC=Local'
$DomainControllers = Get-ADDomainController -Filter {Name -like "*"}
$ErrorActionPreference = 'SilentlyContinue'

$DataAValues = @{}
foreach($DomainController in $DomainControllers) {
    $DomainControllerHostname = $DomainController.HostName
    $dataAParams = @{
        SearchBase  = $OrganizationalUnit
        SearchScope = 'OneLevel'
        Properties  = 'samaccountname', 'LastLogon', 'whenCreated', 'distinguishedname','PasswordLastSet'
        Filter      = { LastLogon -le $Time -AND enabled -eq $true -AND whenCreated -le $Time }
        Server      = $DomainControllerHostname
    }
    Get-ADUser @dataAParams |
        Select-Object SamAccountName,
                    Name,
                    @{Name='LastLogon'; Expression={ [DateTime]::FromFileTime($_.lastLogon).ToString('yyyy-MM-dd HH:mm:ss') }},
                    WhenCreated,
                    PasswordLastSet,
                    @{Name='ReferenceDate'; Expression={ $CurrentTimeStamp }},
                    @{Name='ADAccountDaysInactive'; Expression={ ((Get-Date) - ([DateTime]::FromFileTime($_.lastLogon))).Days }},
                    distinguishedname |
        ForEach-Object {
            if ($DataAValues.Contains($_.SamAccountName)) {
                if ($DataAValues[$_.SamAccountName].LastLogon -lt $_.LastLogon) {
                    $DataAValues[$_.SamAccountName].LastLogon = $_.LastLogon
                }
            } else {
                $DataAValues.Add($_.SamAccountName, $_)
            }
        }
}
$DataA = $DataAValues.Values | Select-Object *

Open in new window

Hi


I think you should try the following which is  based on Obda script   , it does the same as your script  , and now outputs the same fields
so you will not need to change any surrounding code. the only thing to change would be the filter on line 5  to include the the following from

-Filter {Enabled -eq $true}  
to

-Filter {LastLogon -le $Time -AND enabled -eq $true -AND whenCreated -le $Time }


$SearchBase = "OU=Whatever,DC=domain,DC=local"
$CurrentTimeStamp = (Get-Date).ToString('yyyy-MM-dd HH:mm:ss')
$DataA = Get-ADDomainController -Filter * | Select-Object -ExpandProperty HostName  | ForEach-Object {
	"Processing $_ ..." | Write-Host
	Get-ADUser -Server $_ -SearchBase $SearchBase -Filter {Enabled -eq $true} -Properties Name, LastLogon,whenCreated,distinguishedname,PasswordLastSet|
		Select-Object Name ,
		@{Name='LastLogon'; Expression={ [DateTime]::FromFileTime($_.lastLogon).ToString('yyyy-MM-dd HH:mm:ss') }},
		whencreated,Passwordlastset,@{Name='ReferenceDate'; Expression={ $CurrentTimeStamp }},
        @{Name='ADAccountDaysInactive'; Expression={ ((Get-Date) - ([DateTime]::FromFileTime($_.lastLogon))).Days }},
        distinguishedname 
} | Group-Object -Property SamAccountName | ForEach-Object { 	$_.Group | sort lastlogon,name -desc  } | sort distinguishedname -Unique 

$DataA

Open in new window

Hi Joe,

That might be a smart idea, but the whole point of the script is to identify users that have not logged on for at least 60 days. So if I adjust the filter to your suggestion, this will no longer be the case and I will see the LastLogon for very user in the specified OU, which is not the purpose of the script. But I appreciate your suggestion!
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Anyone ideas?
Well, have you tested m yrevised script at https:#a42108648 ?
I have tested it successfully on my Domain controllers.

Somehow it returns one record that was dated back year 1601 ?
That account never logged on.
Great, in that case I'd say the script does works correctly.

Thanks for sharing.
You are very helpful in this forum.
Hi Obda,

I have tested your script and I can confirm that it does not return the most recent logon time. In the export I have UserABC with a logon time in January 2017, but when I query my Domain Controllers I have at least one Domain Controller with a logon time in April 2017 for this UserABC.
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Hi

 did you actually try my script as it did work for and returned the correct results here it is again I have included the filter statement

Please try and let me know your results

$SearchBase = "OU=Whatever,DC=domain,DC=local"
$CurrentTimeStamp = (Get-Date).ToString('yyyy-MM-dd HH:mm:ss')
$minage = (Get-Date).adddays(-60)
$DataA = Get-ADDomainController -Filter * | Select-Object -ExpandProperty HostName  | ForEach-Object {
	"Processing $_ ..." | Write-Host
	Get-ADUser -Server $_ -SearchBase $SearchBase -Filter {LastLogon -le $minage -and enabled -eq $true -AND whenCreated -le $minage } -Properties Name, LastLogon,whenCreated,distinguishedname,PasswordLastSet|
		Select-Object Name ,
		@{Name='LastLogon'; Expression={ [DateTime]::FromFileTime($_.lastLogon).ToString('yyyy-MM-dd HH:mm:ss') }},
		whencreated,Passwordlastset,@{Name='ReferenceDate'; Expression={ $CurrentTimeStamp }},
        @{Name='ADAccountDaysInactive'; Expression={ ((Get-Date) - ([DateTime]::FromFileTime($_.lastLogon))).Days }},
        distinguishedname 
} | Group-Object -Property SamAccountName | ForEach-Object { 	$_.Group | sort lastlogon,name -desc  } | sort distinguishedname -Unique 

$DataA

Open in new window

Thanks both for your help! However, neither solution worked appropriately.

@OBda: When I try your script, it returns for most users the logon times for each Domain Controller. It's a step in the good direction, but not exactly what I'm looking for. Also, for a number of users I only get to see the logon time from a single Domain Controller, which also happens to be the oldest logon date of all. So for example, I have a user named UserA and that user has two logon times, one on DC01 and one on DC02. In the results of your script, for some users, I see for UserA only the logon time from DC01, while DC02 is the most recent one.

@Joe Klimis: It is not working properly in my environment (Windows Server 2008 R2). In the resulting export I have a user with a logon time showing its last logon time was in January, while I have verified using PowerShell that another Domain Controller has a more recent logon time for this user in April.
ASKER CERTIFIED SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Excellent!!! Thank you very much Obda, now it makes completely sense. Tested it & verified that it returned only the most recent logon time for each users. Perfect, thanks a lot.