Recently I was asked how to show all logged on users. So I had the idea to make a function out of it. And now I’ll share this function to the community. Who logged on to which computer and when? That is the question for this article. Actually, the main question is: Who is currently logged in?
Prerequisite for this article is a tidy and clean Active Directory environment. Why? Because it takes about 4 seconds to query a computer’s logged on user. If there are still computer accounts in the database which are no longer used, it will take needless longer. Especially if you try to query the entire domain. Let’s dive in.
The Goal
The target is a function that shows all logged on users by computer name or OU. It’s also possible to query all computers in the entire domain. Note that this could take some time. In my test environment it took about 4 seconds per computer on average.
In Action
Computer
Getting the logged on user of client01. It’s Petra. She logged in at 06:41 PM.
Get-UserLogon -Computer client01
OU
Let’s say we have an OU Workstations. If you want to retrieve all logged on users of all computers in this OU run
Get-UserLogon -OU 'ou=Workstations,dc=sid-500,dc=com'
The second example shows the current logged on user on all Domain Controllers.
Ok I have to admit that my screen is a little boring. I’m in in a small Active Directory testing environment. 😉
Entire Domain
Which brings me to the last parameter. It’s a switch parameter. Don’t provide a value.
Get-UserLogon -All
Ok, that’s it. I think this is quite helpful for many of us.
The Function Get-UserLogon
And here is the function itself:
function Get-UserLogon { [CmdletBinding()] param ( [Parameter ()] [String]$Computer, [Parameter ()] [String]$OU, [Parameter ()] [Switch]$All ) $ErrorActionPreference="SilentlyContinue" $result=@() If ($Computer) { Invoke-Command -ComputerName $Computer -ScriptBlock {quser} | Select-Object -Skip 1 | Foreach-Object { $b=$_.trim() -replace '\s+',' ' -replace '>','' -split '\s' If ($b[2] -like 'Disc*') { $array= ([ordered]@{ 'User' = $b[0] 'Computer' = $Computer 'Date' = $b[4] 'Time' = $b[5..6] -join ' ' }) $result+=New-Object -TypeName PSCustomObject -Property $array } else { $array= ([ordered]@{ 'User' = $b[0] 'Computer' = $Computer 'Date' = $b[5] 'Time' = $b[6..7] -join ' ' }) $result+=New-Object -TypeName PSCustomObject -Property $array } } } If ($OU) { $comp=Get-ADComputer -Filter * -SearchBase "$OU" -Properties operatingsystem $count=$comp.count If ($count -gt 20) { Write-Warning "Search $count computers. This may take some time ... About 4 seconds for each computer" } foreach ($u in $comp) { Invoke-Command -ComputerName $u.Name -ScriptBlock {quser} | Select-Object -Skip 1 | ForEach-Object { $a=$_.trim() -replace '\s+',' ' -replace '>','' -split '\s' If ($a[2] -like '*Disc*') { $array= ([ordered]@{ 'User' = $a[0] 'Computer' = $u.Name 'Date' = $a[4] 'Time' = $a[5..6] -join ' ' }) $result+=New-Object -TypeName PSCustomObject -Property $array } else { $array= ([ordered]@{ 'User' = $a[0] 'Computer' = $u.Name 'Date' = $a[5] 'Time' = $a[6..7] -join ' ' }) $result+=New-Object -TypeName PSCustomObject -Property $array } } } } If ($All) { $comp=Get-ADComputer -Filter * -Properties operatingsystem $count=$comp.count If ($count -gt 20) { Write-Warning "Search $count computers. This may take some time ... About 4 seconds for each computer ..." } foreach ($u in $comp) { Invoke-Command -ComputerName $u.Name -ScriptBlock {quser} | Select-Object -Skip 1 | ForEach-Object { $a=$_.trim() -replace '\s+',' ' -replace '>','' -split '\s' If ($a[2] -like '*Disc*') { $array= ([ordered]@{ 'User' = $a[0] 'Computer' = $u.Name 'Date' = $a[4] 'Time' = $a[5..6] -join ' ' }) $result+=New-Object -TypeName PSCustomObject -Property $array } else { $array= ([ordered]@{ 'User' = $a[0] 'Computer' = $u.Name 'Date' = $a[5] 'Time' = $a[6..7] -join ' ' }) $result+=New-Object -TypeName PSCustomObject -Property $array } } } } Write-Output $result }
Important Note regarding the Operating System Language
Be aware that the function above uses the quser command that outputs plain text. There are differences between e.g. German servers and English servers. This means, that you’ll get the output shown above only on Englisch operating systems.
Make it permanent
If you like my approach open PowerShell ISE. Copy the function into your ISE session. Create a folder in C:\Program Files\Windows PowerShell\Modules and save the code as psm1 file. Make sure that your file name and folder name match.
From now on, PowerShell will load the custom module each time PowerShell is started.
Acknowledements
Thanks to Jaap Brasser (MVP) for his awesome function Get-LoggedOnUser. His function was a great help for me and it inspired me to get a step further and call all logged on users by OU or the entire domain.
His function can be found here:
https://gallery.technet.microsoft.com/scriptcenter/Get-LoggedOnUser-Gathers-7cbe93ea
Categories: PowerShell, Windows Server
Because Invoke-Command accepts a string array into -ComputerName you should be able to speed this up and make it multi-thread
LikeLike
query session /server:pcname
you also can use get-adcomputer and foreach ($computer ….) {query…}
😉
LikeLike
Get-LoggedOnUser will be apt instead of Get-UserLogon
LikeLike
Hi!
Thanks for the suggestion!
Best,
P
LikeLike
Modification to use usernname with blank space
if (($b[1] -match “console”) -or ($b[1] -match “rdp”)) { $Nombre = $b[0] ; $Desplazamiento = 0 }
elseif (($b[2] -match “console”) -or ($b[1] -match “rdp”)) { $Nombre = $b[0]+” “+$b[1] ; $Desplazamiento = 1 }
elseif (($b[3] -match “console”) -or ($b[1] -match “rdp”)) { $Nombre = $b[0]+” “+$b[1]+” “+$b[2] ; $Desplazamiento = 2 }
$array= ([ordered]@{
‘User’ = $Nombre
‘Computer’ = $Computer
‘Date’ = $b[5+$desplazamiento]
‘Time’ = $b[6+$desplazamineto] + $b[7+$desplazamiento] + ‘ ‘
LikeLike
Hi
I am getting the below, Anything I am doing wrong here ?
Get-UserLogon : The term ‘Get-UserLogon’ is not recognized as the name of a cmdlet, function,
script file, or operable program. Check the spelling of the name, or if a path was included,
verify that the path is correct and try again.
LikeLike
I tried this but it only came back on all 25 computers in my Domain with myself logged in at my desktop.
LikeLike
My computer, the cmdlet Get-UserLogon does not work.
It’s not found..
PS version 5.
Ad windows 2012
LikeLike
Hi,
Double check the file store location.
LikeLike
I’ve been trying *reliably* to get remote users for all of our workstations without using remote execution (we’re not allowed to turn it on). If I use quser, it won’t give me the user’s domain; if I try to combine WMI queries with Win32_LoggedOnUser and Win32_LogonSession, I get too much. It returns logged on users that have, in fact, logged off. I am tearing my hair out about it! If anyone knows of a *reliable* method to:
1) get all and *only* currently logged users on remote machines;
2) get their domain (including local logons) together with the username;
3) run it under Powershell without remote execution …
… I will be at least eternally grateful!
LikeLike
John, I use the following command in my scripts to get exactly what you are looking for. It works as part of a map to see all currently logged on users in my environment. Hope it helps.
Get-WmiObject -class win32_computersystem -ComputerName ‘computername’ | Select-Object Username
or if you want just the domain\username without a header you can use:
Get-WmiObject -class win32_computersystem -ComputerName ‘computername’ | ForEach-Object {$_.Username}
LikeLiked by 1 person
Unfortunately, this gives false negatives. In the case of our 1800 workstations that I manage, generally there will be perhaps 10-20 that reply with no ‘UserName’ property, but which, in fact, have a logged user. I have found that in many – but not all – such cases, the computer is sitting waiting for an update from a Windows update having been applied.
I have tried a number of alternate means. Combining Win32_LoggedOnUser with Win32_LogonSession over reports – showing existing logons for users who have, in fact, logged off. I tried also looking for Explorer.exe processes – which works except for disconnected but still logged-on users.
jj
LikeLike