The Win32_LoggedOnUser
class gives a list of logged on users to a computer. It isn’t just users logged on interactively to the console or Remote Desktop (which is a good thing) but even users connected remotely via shared folders and such (not a good thing).
But just for the curious (and since I fiddled with it today) here’s how you can format the output of this class properly.
You can use PowerShell to get instances of this class via the Get-WMIObject
cmdlet thus:
1 |
gwmi Win32_LoggedOnUser |
The output has a lot of material, so best to filter by the Antecedent
property.
1 |
gwmi Win32_LoggedOnUser | ft Antecedent |
Instead of Format-Table
you may use Select-Object
or anything else you fancy.
To query remote computers using the -ComputerName
switch.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
PS> gwmi Win32_LoggedOnUser -ComputerName GARFIELD | Select-Object Antecedent .rootcimv2:Win32_Account.Domain="GARFIELD",Name="SYSTEM" .rootcimv2:Win32_Account.Domain="GARFIELD",Name="LOCAL SERVICE" .rootcimv2:Win32_Account.Domain="GARFIELD",Name="NETWORK SERVICE" .rootcimv2:Win32_Account.Domain="MYDOMAIN",Name="xrakheshx" .rootcimv2:Win32_Account.Domain="MYDOMAIN",Name="xrakheshx" .rootcimv2:Win32_Account.Domain="MYDOMAIN",Name="xrakheshx" .rootcimv2:Win32_Account.Domain="MYDOMAIN",Name="xrakheshx" .rootcimv2:Win32_Account.Domain="MYDOMAIN",Name="xrakheshx" .rootcimv2:Win32_Account.Domain="MYDOMAIN",Name="user1" .rootcimv2:Win32_Account.Domain="MYDOMAIN",Name="user1" .rootcimv2:Win32_Account.Domain="MYDOMAIN",Name="user1" .rootcimv2:Win32_Account.Domain="GARFIELD",Name="ANONYMOUS LOGON" |
The output isn’t precise. You get a list of local accounts as well, and there are repeat entries.
In my case I want to limit to the domain accounts. Also I want to skip accounts starting and ending with the “x” character (as these are my admin accounts) and any accounts entering with a “$” character (as these are machine accounts). And I want to eliminate the duplicates. So here’s what I did:
1 2 3 |
gwmi Win32_LoggedOnUser -ComputerName GARFIELD | ` where-object { (($_.Antecedent -match "MYDOMAIN") -and ($_.Antecedent -notmatch '.*Name="x.+x"') -and ($_.Antecedent -notmatch '.*$')) } | ` %{ $_.Antecedent -replace '.*Name="(.*)"','$1' } | select-object -Unique |
Once again, the beauty of PowerShell is that you can glue things together. So, for instance, if I want to get a list of machines in my domain along with the currently logged in users, I can do that.
To get a list of machines in a particular OU I use the following (this requires the ActiveDirectory
module to be imported first!):
1 |
Get-ADComputer -SearchBase "OU=Muscat,OU=Win_7,OU=computers,DC=rakhesh,DC=com" -Filter * |
I already have the code for getting the logged in user on a machine, so all I need to do is pipe the output of the Get-ADComputer
to Format-Table
and tell it to populate the user column dynamically. Like thus:
1 2 |
Get-ADComputer -SearchBase "OU=Muscat,OU=Win_7,OU=computers,DC=rakhesh,DC=com" -Filter * | ` ft Name,@{Name="Logged in User"; Expression={ gwmi Win32_LoggedOnUser -ComputerName $_.Name | where-object { (($_.Antecedent -match "MYDOMAIN") -and ( $_.Antecedent -notmatch '.*Name="x.+x"') -and ($_.Antecedent -notmatch '.*$')) } | %{ $_.Antecedent -replace '.*Name="(.*)"','$1' } | select-object -Unique } } |
The code within the Expression
block is similar to the code for querying a single machine, except that I replace the machine name with $_.Name
.
The output is a list of machines with the logged in user(s). If there are no logged in users that field is empty {}
else it’s a single user name or a list of user names.
Of course, the Win32_LoggedOnUser
class isn’t useful if users access each other’s machines via shared folders and such; but in that case it’s just a matter of replacing the Expression
block above with code that gives more focussed results. Two great posts on how to list logged in users on a machine are this and this.
I wanted to expand the code above to also include IP addresses too. A PowerShell one-liner to get IP address from a name is the following:
1 2 3 4 5 6 7 8 9 |
PS> [System.Net.Dns]::GetHostAddresses("www.msn.com") IPAddressToString : 207.68.173.76 Address : 1286423759 AddressFamily : InterNetwork ScopeId : IsIPv6Multicast : False IsIPv6LinkLocal : False IsIPv6SiteLocal : False |
To limit the output to just the IP address do the following:
1 |
PS> [System.Net.Dns]::GetHostAddresses("www.msn.com") | Select IPAddressToString -ExpandProperty IPAddressToString |
And to restrict to just IPv4 addresses add the following:
1 |
PS> [System.Net.Dns]::GetHostAddresses("www.msn.com") | where-object { $_.AddressFamily -eq "InterNetwork" } | Select IPAddressToString -ExpandProperty IPAddressToString |
Going by these it’s pretty obvious what we have to do to add an IP address column:
1 2 |
Get-ADComputer -SearchBase "OU=Muscat,OU=Win_7,OU=computers,DC=rakhesh,DC=com" -Filter * | ` ft Name,@{Name="Logged in User"; Expression={ gwmi Win32_LoggedOnUser -ComputerName $_.Name | where-object { (($_.Antecedent -match "MYDOMAIN") -and ( $_.Antecedent -notmatch '.*Name="x.+x"') -and ($_.Antecedent -notmatch '.*$')) } | %{ $_.Antecedent -replace '.*Name="(.*)"','$1' } | select-object -Unique } },@{Name="IP Address"; Expression={ [System.Net.Dns]::GetHostAddresses($_.Name) | where-object { $_.AddressFamily -eq "InterNetwork" } | Select IPAddressToString -ExpandProperty IPAddressToString } } |