These commands are designed for Windows 10 and 11.
We were previously using a simple shellcommandtextreturn with quser to get logged in user information. However, KACE strips multiple spaces and doesn't handle the output formatting very well, so I rewrote it by combining several scripts off the internet and some custom code. Now it will parse the quser results into a powershell object, do a search of the Security event log for each user returned and return the latest 4624 event, parse the event type, and output everything in a much more readable format.
ShellCommandTextReturn(c:\windows\system32\WindowsPowerShell\v1.0\powershell.exe -executionpolicy bypass -Command "$LoggedIn=@();$qu=((((quser) -replace '\s{20,39}',',,') -replace '\s{2,}',',') -replace '>',''|ConvertFrom-Csv);Foreach($cur in $qu){$UserName=$cur.UserName;$UserEvent=Get-WinEvent -LogName \"Security\" -FilterXPath \"*[System[EventID=4624]]\"|Where-Object{($_.Properties[5].Value -like $UserName)}|Select -First 1;switch($UserEvent.Properties[8].Value){2{$ActivityType='Physical Login'};7{$ActivityType='Computer Unlock'};10{$ActivityType='Remote Login'};11{$ActivityType='Cached Login'};};$LoggedIn+=[pscustomobject]@{User=$UserName;LastLogonTime=$cur.'Logon Time';LastActivityType=$ActivityType;LastActivityTime=$UserEvent.TimeCreated;CurrentlyActive=$cur.State;IdleTime=$cur.'Idle Time';}};$LoggedIn")
This will return results like:
User : bnewland
LastLogonTime : 8/21/2024 9:42 AM
LastActivityType : Computer Unlock
LastActivityTime : 9/18/2024 7:57:33 AM
CurrentlyActive : Active
IdleTime : none
I should note that if the client OS is not English, this will fail to work. Research online showed that it should be possible to run:
Invoke-Expression 'cmd /c "chcp 437 > nul: & quser"'
but I was unable to figure out the proper escape characters to get this to run properly. As we only have English Windows in our organization, I chose not to invest time in figuring this out.
Our All User Profiles is a new one, also created by combining various code sources with custom coding. Most methods (including built-in Windows methods) use the last modified date of ntuser.dat, which is very unreliable. I eventually found that the GPO for removing old user profiles was updated to use some registry keys in the user registry hive that record the last time the user hive was loaded and unloaded (basically when the user last logged in and out). If the user is currently logged in, the LastLogon will be newer than the LastLogoff.
ShellCommandTextReturn(c:\windows\system32\WindowsPowerShell\v1.0\powershell.exe -executionpolicy bypass -Command "$pl=GCI \"HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\";$userlist=@();foreach($p in $pl){try{$objUser=(New-Object System.Security.Principal.SecurityIdentifier($p.PSChildName)).Translate([System.Security.Principal.NTAccount]).value}catch{$objUser=\"UNKNOWN\"};if(($objUser -notlike \"NT*\") -and ($objUser -notmatch \"UNKNOWN\")){Remove-Variable -Force LTH,LTL,UTH,UTL -EA 0;$LTH='{0:X8}' -f (GP -Path $p.PSPath -Name LocalProfileLoadTimeHigh -EA 0).LocalProfileLoadTimeHigh;$LTL='{0:X8}' -f (GP -Path $p.PSPath -Name LocalProfileLoadTimeLow -EA 0).LocalProfileLoadTimeLow;$UTH='{0:X8}' -f (GP -Path $p.PSPath -Name LocalProfileUnloadTimeHigh -ErrorAction SilentlyContinue).LocalProfileUnloadTimeHigh;$UTL='{0:X8}' -f (GP -Path $p.PSPath -Name LocalProfileUnloadTimeLow -ErrorAction SilentlyContinue).LocalProfileUnloadTimeLow;$LoadTime=if($LTH -and $LTL){[datetime]::FromFileTime(\"0x$LTH$LTL\")}else{$null};$UnloadTime=if($UTH -and $UTL){[datetime]::FromFileTime(\"0x$UTH$UTL\")}else{$null};$userlist+=[pscustomobject]@{User=$objUser;LastLogon=$LoadTime;LastLogoff=$UnloadTime;}}};$userlist|sort User|fl")
This will return results like:
User : PCName\Admin
LastLogon : 4/4/2023 3:01:39 PM
LastLogoff : 4/4/2023 3:29:24 PM
User : PCName\Administrator
LastLogon :
LastLogoff :
User : MyDomain\BNewland
LastLogon : 8/21/2024 9:42:30 AM
LastLogoff : 8/21/2024 9:26:39 AM
User : MyDomain\bnewland_admin
LastLogon : 8/21/2024 9:45:33 AM
LastLogoff : 8/21/2024 9:45:33 AM
User : MyDomain\dell
LastLogon : 9/16/2024 11:14:39 AM
LastLogoff : 9/16/2024 11:14:39 AM
ShellCommandTextReturn(c:\windows\system32\WindowsPowerShell\v1.0\powershell.exe -executionpolicy bypass -Command "$LoggedIn=@();$qu=((((C:\Windows\sysnative\quser.exe) -replace '\s{20,39}',',,') -replace '\s{2,}',',') -replace '>',''|ConvertFrom-Csv);Foreach($cur in $qu){$UserName=$cur.Benutzername;$UserEvent=Get-WinEvent -LogName \"Security\" -FilterXPath \"*[System[EventID=4624]]\"|Where-Object{($_.Properties[5].Value -like $UserName)}|Select -First 1;switch($UserEvent.Properties[8].Value){2{$ActivityType='Physical Login'};7{$ActivityType='Computer Unlock'};10{$ActivityType='Remote Login'};11{$ActivityType='Cached Login'};};$LoggedIn+=[pscustomobject]@{User=$UserName;Anmeldezeit=$cur.Anmeldezeit;LastActivityType=$ActivityType;LastActivityTime=$UserEvent.TimeCreated;CurrentlyActive=$cur.Status;IdleTime=$cur.Leerlauf;}};$LoggedIn") - TheBatman 1 month ago