Latest last logon

by Chris_J at 2013-04-21 22:20:15

Hi Guys,

Needing some more help again please. I have the below script hashed from the one I asked about the other week, however, all I need is the information that is in the script, but I would like to query my domain controllers for the last logon. The Last Logon Time Stamp works ok, but some come back blank so just wanted that extra check.

[code2=powershell]$InputFile = "C:\Utils\xxx"
$OutputFile = "C:\Utils\xxx"


$Result = @()
$Enabled = ""
$Username = ""
$FN = ""
$DN = ""
$Logon = ""
$LastLogon = ""
$Password = ""
$DisplayName = ""


Import-Csv $InputFile | ForEach-Object {
$User = Get-QADUser $.SamAccountName -SearchRoot "domain blah blah"

if($User -eq $null)
{

$Username = $
.SamAccountName
$FN = "N/A"
$LN = "N/A"
$Enabled = "User not found"
$Logon = "N/A"
$LastLogon = "N/A"
$Password = "N/A"
$DisplayName = "N/A"

}

else

{
$Username = $User.SamAccountName
$FN = $User.FirstName
$LN = $User.LastName
$Enabled = $User.AccountIsDisabled
$Logon = $User.LastLogonTimeStamp
$LastLogon = $User.LastLogon
$Password = $User.PasswordLastSet
$DisplayName = $User.DisplayName
}

$Result += New-Object PSObject -Property @{
User = $Username
FN = $FN
LN = $LN
Enabled = $Enabled
LastLogonTimeStamp = $Logon
LastLogon = $LastLogon
DisplayName = $DisplayName
PasswordSet = $Password

}
}
$Result | Out-GridView | Sort-Object User
$Result | Select-Object User, FN, LN, Enabled, LastLogon, LastLogonTimeStamp | Sort-Object User | Export-Csv $OutputFile -NoTypeInformation[/code2]
by Klaas at 2013-04-22 00:59:27
[quote="Chris_J"]the one I asked about the other week[/quote]
We don’t really memorize all posts here,…

I suppose you struggle with the difference between lastlogon and lastlogontimestamp, as in this topic:
viewtopic.php?f=9&t=653&p=2581&hilit=lastlogon#p2581

In short, if you really want the last logon time, you have to loop through all domain controllers, ask every one for the lastlogontimestamp of every user and extract the most recent per user. When there are blanks, that means this user has never logged on on that specific domain controller. Lastlogon is replicated, but not immediately.
by Chris_J at 2013-04-22 16:20:02
I know the difference between last logon and last logon time stamp. However, for all I know I could have users who have only authenticated on one DC in the last few days, yet their last logon time stamp is blank. All I really need to know, is how to build in a query to my script to check the lastlogon on my DCs and output the latest one.
by mjolinor at 2013-04-22 16:31:51
[quote] All I really need to know, is how to build in a query to my script to check the lastlogon on my DCs and output the latest one.[/quote]

There’s multiple ways to do that, and which one is appropriate will depend on the script it’s being built into. Whatcha got?
by Chris_J at 2013-04-22 17:31:56
It’s the script above. I can’t get it to query each DC properly. I don’t know how to do a ForEach-Object to get the DC names and then a ForEach-Object for the SamAccountNames in my csv file to bring back the info for each user on each DC.
by Chris_J at 2013-04-22 18:00:44
So this is what I have so far, which gives me the info from all DCs. What I am getting though at the top of the spreadsheet is System.Object don’t know how or why.

Is there any way I could filter to just show the information for the lastest last logon, and what am I doing wrong with this script? :slight_smile:

P.S. We only have 3 DCs

[code2=powershell]$InputFile = "C:\Utils\xxx.csv"
$OutputFile = "C:\Utils\yyy.csv"


$old = (Get-Date).AddDays(-60)

$Result = @()
$NotFoundCount = 0
$FoundCount = 0
$Over60DaysCount = 0
$Enabled = ""
$Username = ""
$FN = ""
$DN = ""
$Logon = ""
$LastLogon = ""
$Password = ""
$DisplayName = ""
$Current = ""
$DomainController = ""


Get-QADComputer -ComputerRole DomainController | Foreach-Object{
$dc = $.Name

Import-Csv $InputFile | ForEach-Object {
$User = Get-QADUser $
.SamAccountName -Service $dc

if($User -eq $null)

{ $Username = $.SamAccountName
$FN = "N/A"
$LN = "N/A"
$Enabled = "User not found"
$Logon = "N/A"
$LastLogon = "N/A"
$Password = "N/A"
$DisplayName = "N/A"
$Current = "N/A"
$DomainController = "N/A"
$NotFoundCount ++ }

else

{

if ($User.LastLogonTimestamp -le $old)

{ $Username = $User.SamAccountName
$FN = $User.FirstName
$LN = $User.LastName
$Enabled = $User.AccountIsDisabled
$Logon = $User.LastLogonTimestamp
$LastLogon = $User.LastLogon
$Password = $User.PasswordLastSet
$DisplayName = $User.DisplayName
$Current = "Over 60 Days"
$DomainController = $dc
$Over60DaysCount ++
$FoundCount ++ }

else


{ $Username = $User.SamAccountName
$FN = $User.FirstName
$LN = $User.LastName
$Enabled = $User.AccountIsDisabled
$Logon = $User.LastLogonTimeStamp
$LastLogon = $User.LastLogon
$Password = $User.PasswordLastSet
$DisplayName = $User.DisplayName
$Current = "Yes"
$DomainController = $dc
$FoundCount ++ }

}

$Result += New-Object PSObject -Property @{
User = $Username
FN = $FN
LN = $LN
Disabled = $Enabled
LastLogonTimeStamp = $Logon
LastLogon = $LastLogon
DisplayName = $DisplayName
PasswordSet = $Password
Current = $Current
DomainController = $DomainController }
}
}

$Result | Out-GridView | Sort-Object User
$Result | Select-Object User, FN, LN, Displayname, Disabled, LastLogon, LastLogonTimeStamp, PasswordSet, Current, DomainController | Sort-Object User | Export-Csv $OutputFile -NoTypeInformation

"Accounts in file: $((Get-Content $InputFile).Length -1)"
"Users Found: $FoundCount"
"Users Not Found: $NotFoundCount"
"Account Over 60 Days: $Over60DaysCount"[/code2]
by ArtB0514 at 2013-04-23 06:50:13
I haven’t had a chance to try to sample your script, but looking at it, there are a small number of suggestions I’d like to make.
[list]You’re reading in the list of users for each domain controller. If there are a lot of domain controllers (we have well over 100 in our global network), then you’ll be spending a lot of redundant time doing that. Read it in once and save it in a variable.
There’s an ElseIf construct that can make your code a bit easier to read: If (){} Elseif (){} ElseIf (){} Else {}
Your pipeline is a bit out of order at line 93; do the sort before the Out-GridView.[/list]
And Good-On-You for just letting the strings at the end of your script output themselves instead of using Out-Host.

As to your problem with the System.Object[], is it in your GridView or the CSV file, or both? If only in the CSV file, try removing the Select-Object clause to see if that’s where the error is.

As for only having the last logon value, you can do that in Excel by sorting on the User (A to Z) and the LastLogon (Newest to Oldest) and then RemoveDuplicates. If you want to do it in PowerShell, then, run the $Results object through Group-Object on Name then for each $
.Group sorted on LastLogon, select the last one ($.Group[-1]) and save that to a new collection.
by Chris_J at 2013-04-23 15:06:43
Thank You
by Chris_J at 2013-04-23 19:49:04
I’m struggling to create the variable to be read once, just don’t know how to do it.
by ArtB0514 at 2013-04-24 06:36:38
Snippet:
$Users = Import-Csv $InputFile
Get-QADComputer -ComputerRole DomainController | Foreach-Object {
$dc = $
.Name
$Users | ForEach-Object {
$User = Get-QADUser $.SamAccountName -Service $dc
by Chris_J at 2013-04-28 18:49:45
That works well, Thank you, however it is still pretty slow unless that’s normal. Do I need to refine the Get-QADUser to find specifics? I am searching about 200 users though.
by ArtB0514 at 2013-04-29 08:26:32
Get-QADUser returns quite a bit of information by default, much more than the Microsoft Get-ADUser does. You’ll need to check the properties returned and only modify the command if there’s something you want that’s not provided by default.

This process is slow, pretty much by definition, because you’re making so many calls to Get-QADUser. Depending on the number of users and the number of domain controllers, you might get better performance by traversing the domain controllers and collecting all users from each. Doing this, though, trades off memory for speed, so I wouldn’t try it on a 32 bit system.
by Chris_J at 2013-05-02 21:35:58
So at long last I got it doing what I want.

[code2=powershell]$InputFile = "C:\Utils\xxxx.csv"
$OutputFile = "C:\utils\yyyy.csv"


$old = (Get-Date).AddDays(-120)

$Result = @()
$FoundCount = 0
$NotFoundCount = 0
$Over120DaysCount = 0
$NeverLoggedOnCount = 0
$Enabled = ""
$Username = ""
$FN = ""
$DN = ""
$Logon = ""
$LastLogon = ""
$DisplayName = ""
$Current = ""
$DomainController = ""
$UserDN = ""

$Userlist = Import-Csv $InputFile

Get-QADComputer -ComputerRole DomainController | ForEach-Object {
$dc = $
.Name

$Userlist | ForEach-Object {
$User = Get-QADUser $.SamAccountName -Service $dc -SizeLimit 0


If ($User -eq $null)


{ $Username = $
.SamAccountName
$FN = $User.FirstName
$LN = $User.LastName
$Enabled = $User.AccountIsDisabled
$Logon = $User.LastLogonTimestamp
$LastLogon = $User.LastLogon
$DisplayName = "Not a valid user"
$Current = "N/A"
$DomainController = $dc
$UserDN = $User.DN
$NotFoundCount ++ }


ElseIf ($User.LastLogonTimestamp -eq $null)


{ $Username = $User.SamAccountName
$FN = $User.FirstName
$LN = $User.LastName
$Enabled = $User.AccountIsDisabled
$Logon = $User.LastLogonTimestamp
$LastLogon = $User.LastLogon
$DisplayName = $User.DisplayName
$Current = "Never Logged On"
$DomainController = $dc
$UserDN = $User.DN
$NeverLoggedOnCount ++
$FoundCount ++ }


ElseIf ($User.LastLogonTimestamp -le $old)

{ $Username = $User.SamAccountName
$FN = $User.FirstName
$LN = $User.LastName
$Enabled = $User.AccountIsDisabled
$Logon = $User.LastLogonTimestamp
$LastLogon = $User.LastLogon
$DisplayName = $User.DisplayName
$Current = "Over 120 Days"
$DomainController = $dc
$UserDN = $User.DN
$Over120DaysCount ++
$FoundCount ++ }

Else

{ $Username = $User.SamAccountName
$FN = $User.FirstName
$LN = $User.LastName
$Enabled = $User.AccountIsDisabled
$Logon = $User.LastLogonTimestamp
$LastLogon = $User.LastLogon
$DisplayName = $User.DisplayName
$Current = "Yes"
$DomainController = $dc
$UserDN = $User.DN
$FoundCount ++ }



$Result += New-Object PSObject -Property @{
User = $Username
FN = $FN
LN = $LN
Disabled = $Enabled
LastLogonTimeStamp = $Logon
LastLogon = $LastLogon
Current = $Current
DisplayName = $DisplayName
DC = $DomainController
OU = $UserDN}
}
}

$Result | Select-Object User, DisplayName, LastLogon, LastLogonTimeStamp, FN, LN, Disabled, Current, DC, OU |
sort @{expression="User";Ascending=$true},@{expression="LastLogon";Descending=$true} |
Group-Object -Property User | ForEach-Object {$.Group[ 0 ]} | Out-GridView

$Result | Select-Object User, DisplayName, LastLogon, LastLogonTimeStamp, FN, LN, Disabled, Current, DC, OU |
sort @{expression="User";Ascending=$true},@{expression="LastLogon";Descending=$true} |
Group-Object -Property User | ForEach-Object {$
.Group[ 0 ]} | Export-Csv $OutputFile -NoTypeInformation

"Accounts in file: $((Get-Content $InputFile).Length -1)"
"Users Found: $(($FoundCount)/3)"
"Users Not Found: $(($NotFoundCount)/3)"
"Account Over 120 Days: $(($Over120DaysCount)/3)"
"Never Logged On: $(($NeverLoggedOnCount)/3)"[/code2]

We have 3 DCs hence why I’m dividing the results by 3. I’ve managed to sort the results by User ID and then Last Logon and choose the first in each group. Seems to work. I’m undecided whether I should choose the properties to search for at the top. If I do that, how do I keep the output in the order I have it? Trying to learn good practice of filter left, format right.