/build/static/layout/Breadcrumb_cap_w.png

KACE SMA: Custom Inventory Rules for retrieving user profiles and logged in users

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


Comments

  • Thanks for the rule, this rule works on German Windows:

    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 4 days ago
This post is locked

Don't be a Stranger!

Sign up today to participate, stay informed, earn points and establish a reputation for yourself!

Sign up! or login

Share

 
This website uses cookies. By continuing to use this site and/or clicking the "Accept" button you are providing consent Quest Software and its affiliates do NOT sell the Personal Data you provide to us either when you register on our websites or when you do business with us. For more information about our Privacy Policy and our data protection efforts, please visit GDPR-HQ