A common task any Windows admin might have is finding out, locally or remotely, which user account is logged onto a particular computer. Many tools exist for this purpose, and one of them, of course, is PowerShell.

A Windows admin might need this information to create reports, to track down malware infection or to see who's in the office. Since this is a repeatable task, it's a good idea to build a script that you can reuse over and over again, rather than having to figure out how to do it every time.

In this article, I'm going to go over how to build a PowerShell script to find a logged-on user on your local Windows machine, as well as on many different remote Windows machines at once. By the end, you should have a good understanding of what it takes to query the logged-on user of a Windows computer. You will also understand how to build a PowerShell script to execute the command on multiple computers at the same time.

With PowerShell, getting the account information for a logged-on user of a Windows machine is easy, since the username is readily available using the Win32_ComputerSystem WMI instance. This can be retrieved via PowerShell by using either the Get-CimInstance or Get-WmiObject cmdlet. I prefer to use the older Get-WmiObject cmdlet because I’m still working on older machines.

Get-WmiObject –ComputerName CLIENT1 –Class Win32_ComputerSystem | Select-Object UserName
Output

Output

If you prefer to use CIM, you can also use Get-CimInstance to return the same result.

Get-CimInstance –ComputerName CLIENT1 –ClassName Win32_ComputerSystem | Select-Object UserName

End of article, right? I suppose you could say I did just show you how to discover a logged-on user remotely. However, we need to make this reusable, more user-friendly and easy to perform on multiple computers. Let's take it a step further and build a PowerShell function from this.

First, let's build our template function. It looks like this:

function Get-LoggedOnUser
 {
     [CmdletBinding()]
     param
     (
         [Parameter()]
         [ValidateScript({ Test-Connection -ComputerName $_ -Quiet -Count 1 })]
         [ValidateNotNullOrEmpty()]
         [string[]]$ComputerName = $env:COMPUTERNAME
     )
 }

Here, we have an advanced function with a single parameter: ComputerName. We also want to incorporate some parameter validations to ensure that the computer responds to a ping request before we query it. Also, notice the parameter type: [string[]]. Notice how there is an extra set of brackets in there? This makes ComputerName a string collection, rather than just a simple string. This is going to allow us to specify multiple computer names, separated by commas. We’ll see how this comes into play a bit later.

Once we have the function template down, we’ll need to add some functionality. To do that, let’s add a foreach loop, in case $ComputerName has multiple computer names, and then create a custom object for each computer, querying each for the logged-on user.

function Get-LoggedOnUser
 {
     [CmdletBinding()]
     param
     (
         [Parameter()]
         [ValidateScript({ Test-Connection -ComputerName $_ -Quiet -Count 1 })]
         [ValidateNotNullOrEmpty()]
         [string[]]$ComputerName = $env:COMPUTERNAME
     )
     foreach ($comp in $ComputerName)
     {
         $output = @{ 'ComputerName' = $comp }
         $output.UserName = (Get-WmiObject -Class win32_computersystem -ComputerName $comp).UserName
         [PSCustomObject]$output
     }
 }

Here, notice that instead of outputting only the username, we are building a custom object that outputs the computer name as well, so that when multiple computer names are used, I can tell which username coincides with which computer.

Now, let’s run this and see what the output looks like when we don't specify a computer name.

Without specified computer name

Without specified computer name

My local computer name is WINFUSIONVM, and I am logged in through a local account called Adam. Now, let's see what it looks like when we query a remote computer.

Queried a remote computer

Queried a remote computer

In the instance above, notice that the account exists within a domain. We know this because the username starts with MYLAB, rather than MEMBERSRV1.

Finally, let's pass a couple different computer names through this function.

Different computer names

Different computer names

You can see that CLIENT2's UserName is null. This is because no account is currently logged on the computer.

Subscribe to 4sysops newsletter!

If you'd like a fully featured function with error control, feel free to download this function from my Github repo.

+18
avataravatar
30 Comments
  1. Ashish Singh 5 years ago

    For some scenarios, it returns null. I tried running it on a Virtual Machine running Windows 10 (14393) x64.

    +10

  2. Roi 5 years ago

    It lookes like that some things changed:

    Get-WmiObject –ComputerName client01 –Class Win32_ComputerSystem

    This returns no longer a property called UserName

    +3

    • Marc 4 years ago

      Yeah this isn't working for me either.

      +4

    • What is you client OS and powershell version?

      What is the OS and .Net Framework version of the target server?

      +1

    • Arun 7 months ago

      try modify it as

      $output.UserName = (Get-WmiObject -Class win32_computersystem -ComputerName $comp).Name

      0

  3. Dennis 4 years ago

    Hello,

    is there a way to list logged on users from a list createdbefore? I want to create a script  which exports a list of specific domain Computers. Then I want to get the logged on users and the information when the machine last bootet up. The result of this should be outputet as a csv.

    Could might help me out with this?

    I tired this without success:

    #load AD module
    Import-Module activedirectory
    
    
    #create machine list
    write-progress -activity "creating list of machines" -status "please wait..."
    $Machinelist=Get-ADComputer -filter {name -Like "de1-v*"} | select Name
    
    
    # function get loggedon users
    foreach(Machine in Machinelist)
    {
    function Get-LoggedOnUser
     {
     [CmdletBinding()]
     param
     (
     [Parameter()]
     [ValidateScript({ Test-Connection -ComputerName $Machine -Quiet -Count 1 })]
     [ValidateNotNullOrEmpty()]
     [string[]]$ComputerName = $env:COMPUTERNAME
     )
     foreach ($comp in $ComputerName)
     {
     $output = @{ 'ComputerName' = $comp }
     $output.UserName = (Get-WmiObject -Class win32_computersystem -ComputerName $comp).UserName
     [PSCustomObject]$output
     }
     }
    }
    
    # get last boot time
    Get-WmiObject -ComputerName $Machine win32_operatingsystem | select csname, @{LABEL=’LastBootUpTime’;EXPRESSION={$_.ConverttoDateTime($_.lastbootuptime)}}

    I really would appriciate your help.

    best regards

    D

    +1

  4. Author

    You are so close! You just need to create the function that pulls the logged in user from a single computer and then loop over each computer calling the function ad well as the Get-WmiObject reference. Something like this:

    function Get-LoggedInUser {
      param($ComputerName)
      
    }
    
    $computers = Get-AdComputer
    foreach ($comp in $computers) {
      $output = @{}  
      $output.UserName = Get-LoggedInUser -ComputerName $comp.Name
      $output.BootUp = Get-WmiObject ###
      [pscustomobject]$output
    
    }
    +3

    • Dennis 4 years ago

      Hello Adam,

      thank you for your reply 🙂

      I'm really at the beginning in writing powershell scripts. What you mean is to replace line 11 to 30 in my script with the code you provided so that it lookslike this?

      #load AD module
      Import-Module activedirectory
       
       
      #create machine list
      write-progress -activity "creating list of machines" -status "please wait..."
      $Machinelist=Get-ADComputer -filter {name -Like "de1-v*"} | select Name
       
       
      # function get loggedon users
      function Get-LoggedInUser {
        param($ComputerName)
        
      }
       
      $computers = Get-AdComputer
      foreach ($comp in $computers) {
        $output = @{}  
        $output.UserName = Get-LoggedInUser -ComputerName $comp.Name
        $output.BootUp = Get-WmiObject ###
        [pscustomobject]$output
       
      }
       
      # get last boot time
      Get-WmiObject -ComputerName $Machine win32_operatingsystem | select csname, @{LABEL=’LastBootUpTime’;EXPRESSION={$_.ConverttoDateTime($_.lastbootuptime)}}
      +1

      • Author

        You will need to include the last bootup time within the foreach loop too so it can gather the information from each computer as it's reading them.

        +2

        • Dennis 4 years ago

          Hi Adam,

          thank you for your reply. So this would be the solution?:

          #load AD module
          Import-Module activedirectory
           
           
          #create machine list
          write-progress -activity "creating list of machines" -status "please wait..."
          $Machinelist=Get-ADComputer -filter {name -Like "de1-v*"} | select Name
           
           
          # function get loggedon users
          function Get-LoggedInUser {
           param($ComputerName)
           
          }
           
          $computers = Get-AdComputer
          foreach ($comp in $computers) {
           $output = @{} 
           $output.UserName = Get-LoggedInUser -ComputerName $comp.Name
           $output.BootUp = Get-WmiObject ###
           [pscustomobject]$output
           
          # get last boot time
          Get-WmiObject -ComputerName $Machine win32_operatingsystem | select csname, @{LABEL=’LastBootUpTime’;EXPRESSION={$_.ConverttoDateTime($_.lastbootuptime)}}
          }

          When I run this script like provided above I get a command line which tells me to enter a parameter without telling me which one. The Screenshot can be found under this link:

          https://www.dropbox.com/s/jrtgyxa1kd1slng/26-06-_2017_17-52-56.png?dl=0

          But I have to say it is in german language.

          thank you very much for your help 🙂

          regards Dennis

          +2

  5. Mauro (Rank: 2)
    4 years ago

    I use:
    invoke-command -computername -scriptblock { qwinsta }
    or:
    qwinsta /server:

    +7

    • PatrikN 4 years ago

      Like some others, it did not work for me either. UserName is just empty, both on Server 2003, 2008 and Win10.

      Qwinsta, as Mauro suggested, did work, but didn't show the device og client name.

      Instead I found the PSTerminalServices module, which gave me exactly the information I wanted, and it can also do a lot more! (To get it installed, I had to unzip and manually rename the MSI file).

      I then ended up with this script (using Get-TSSession), which shows exactly the same information as Task Manager 🙂

      Get-TSSession | Where-Object -FilterScript { $_.UserName -NotLike "" } | Format-Table -AutoSize -Property @{L='User'; E={$_.UserName}}, @{L='ID'; E={$_.SessionID}}, @{L='Status'; E={$_.State}}, @{L='Client Name'; E={$_.ClientName}}, @{L='Session'; E={$_.WindowStationName}}
      

      This can of course be run on remote computers (PSTerminalServices just need to be installed locally) and there is also some commands to get info about TS/RD servers and processes. Here is one mor script, with some extended info.

      Get-TSSession | Where-Object -FilterScript { $_.UserName -NotLike "" } | Format-Table -AutoSize -Property @{L='User'; E={$_.UserName}}, @{L='ID'; E={$_.SessionID}}, @{L='Status'; E={$_.State}}, @{L='IP/Remote endpoint'; E={$_.RemoteEndPoint}}, @{L='Client Name'; E={$_.ClientName}}, @{L='Session'; E={$_.WindowStationName}}, @{L='Idle Time'; E={$_.IdleTime}}, @{L='Logon Time'; E={$_.LoginTime}}, @{L='Connect Time'; E={$_.ConnectTime}}, @{L='Disconnect Time'; E={$_.DisconnectTime}}

      Best regards,
      Patrik

      +1

  6. Eric W 3 years ago

    I am using "$LocalUser = get-wmiobject -class win32_ComputerSystem | Select username" which returns not only the user name but the domain name "Domain\UserName". how do pass this info while removing the domain part from the username?

    +1

  7. Eric W.

    Simple solution:
    $LocalUser = (Get-WmiObject -Class win32_computersystem | Select-Object -ExpandProperty username).split('\')[1]

    David F.

    +4

  8. Blake 3 years ago

    I use powershell and cmd to do this. The code below gets the currently logged on user from windows explorer. It will return more than 1 name if multiple users are currently logged in. Simply give $RemoteHost an IP address or Hostname.

    $RemoteHost = IPAddress/Hostname

    $LoggedInUser = tasklist /s $RemoteHost /v /FI "IMAGENAME eq explorer.exe" /FO list | find "User Name:"
    $LoggedInUser = $LoggedInUser.Substring(14)

    Blake

    +6

  9. Now that I've read the entire post & comments.. the easiest way is certainly without powershell.

    As Mauro pointed out, you can use qwinsta /server:servername, you can also use quser /server:servername.  The benefit of these is you don't require any special permissions on the machine to get the info.   You can also use qwinsta, quser & qprocess on a local machine.. qprocess with no parameters gives you your own processes with no special permissions.

    You can also use qprocess against a remote machine (that *does* require admin permissions, unless it is your own session.  qprocess /id:<sessionid> /server:<server>..

    And of course, there are a number of options to all these commands.

    David F.

    +3

  10. Todd M 3 years ago

    Hello, I have been trying to find a simple script or cmd to see if anyone is logged on to another computer.  I found your reply about the quser being used in a cmd prompt instead of a powershell script and you note that no special authority is needed.  However, when I try it on my own pc, it works fine but when querying somebody else's, it comes up with access denied.  I am using an account that has administrative authority so not sure why access denied is coming up.  I was wondering if you were familiar with the error: 0x00000005 enumerating sessionnames [5]:Access is denied.

    +1

    • bahnjee 2 years ago

      @Todd M,

      I know this is old but to do what you're looking for, use PSLoggedOn.exe from SysInternals (a Microsoft Tool).

      running the command,

      psloggedon \\PCName

      will return something like:

      PsLoggedon v1.35 - See who's logged on

      Copyright (C) 2000-2016 Mark Russinovich
      Sysinternals - http://www.sysinternals.com

      Users logged on locally:
           11/20/2019 2:50:27 PM      domain\username

      Users logged on via resource shares:
           11/20/2019 4:09:13 PM      domain\yourownusername

      0

  11. You do need to belong to the domain or have some authentication between the machines.  I'm guessing you are not in the same domain/workgroup?  You will need *some* sort of authentication between the source & target, but not special permissions beyond normal user permissions.

    I've seen access denied when the accounts were from untrusted accounts, or when trying to use qprocess without admin rights.

    David F.

    +2
    avatar
  12. Ashish 1 year ago

    Hi Team, I am not a user of PowerShell so don’t know how it works.

    i have been told as a one off to get a PowerShell script to find the users logged into our servers. The need is that I run a powershell script which take the server names from excel/ text file and then return the users logged into those servers at that time. If the output in excel then it would be best but if any other format then it’s acceptable as well. I did try the above menthol which did not work for me and other google answers as well but could not succeed.

    is there someone who can help here. Highly appreciate it.

    0

  13. Hi, I need to find a specific AD user account's logged in activity over past 30 days or so into a TS cluster of 3 x Terminal Servers.  I've tried a couple of scripts but can't seem to get it to work how I want it, anyone ever done this before and know how to create a PS script like this?

     

    Thanks!

    0

  14. olijuice 12 months ago

    Thank you for this, it is really helpful! I love how it displays the computer name and user name in two columns. Question... If I had a static list of computers.. for example 10 computers. I want to run a script similar to this that checks to see who is logged in. But instead of displaying a result of the "computername" how can I give it my own friendly name.

    For example below the results would show that the service account 1 is logged into 3 computers. Instead of identifying their computer names it would give it a friendly name like "Stat Box 1" instead of the random computer names they really have.

    Stat Box 1 - Domain\serviceacc1 

    Stat Box 2 - Domain\serviceacc1

    Stat Box 3 - Domain\serviceacc1

    +2
    avataravatar
    • Not exactly sure what is that useful for, but I think you can easily add whatever fields you like. In your case I would assume something like this (not tested)

      $counter = 1  
      foreach ($comp in $ComputerName)
           {
      $output = @{ 'BoxNumber' = "Stat Box $counter" }
               $output = @{ 'ComputerName' = $comp }
               $output.UserName = (Get-WmiObject -Class win32_computersystem -ComputerName $comp).UserName
               [PSCustomObject]$output
      $counter  
           }
      +1
      avatar
  15. olijuice 11 months ago

    Hi Leos, thanks for your reply and sorry I took so long to get back. I was able to find a solution by the code below. It's probably very basic and could be improved but I'm learning as I go.

    The intention of this is that I wrote a PowerShell script that my team can run every morning that checks to see whether about 20 computers are on. If they are on we make sure they are logged in and we also check to make sure they are running specific programs.

    I incorporated this into the script so that we can validate whether the account StatPC is logged in or out or if the computer is even powered on.

    # Pings each computer and if it gets a response then checks to see if UserStats is logged in.
    Function Get-UserLogins {
        If ($Global:PC1 = (Test-Connection -ComputerName StatPC1 -Quiet -Count 1) -eq $True) {
            $Global:PC1 = Get-WmiObject -ComputerName StatPC1 -Class Win32_ComputerSystem | Select-Object UserName
            }
        Else {
            $Global:PC1 = "Failed to ping"
            }
        If ($Global:PC1 -match '@{UserName=}') {
            $Global:PC1 = "Needs to be logged in"
            }
        ElseIf ($Global:PC1 -match 'UserStats') {
            $Global:PC1 = "UserStats is logged in"
            }
        If ($Global:PC2 = (Test-Connection -ComputerName StatPC2 -Quiet -Count 1) -eq $True) {
            $Global:PC2 = Get-WmiObject -ComputerName StatPC2 -Class Win32_ComputerSystem | Select-Object UserName
            }
        Else {
            $Global:PC2 = "Failed to ping"
            }
        If ($Global:PC2 -match '@{UserName=}') {
            $Global:PC2 = "Needs to be logged in"
            }
        ElseIf ($Global:PC2 -match 'UserStats') {
            $Global:PC2 = "UserStats is logged in"
            }
    }
    # Menu to show the results of the Get-UserLogins function
    Function Show-UserLogins {
        ''
        '------------'
        Write-Host 'Ping Results' -BackgroundColor Cyan -ForegroundColor White
        '------------'
        ''
        ''
        ''
        'City 1 Computers'
        '----------------'
        If ($PC1 -match 'Failed to ping') {
            Write-Host "Stat PC 1   - $PC1" -BackgroundColor Red -ForegroundColor White
            }
        ElseIf ($PC1 -match 'Needs to be be logged in') {
            Write-Host "Stat PC 1   - $PC1" -BackgroundColor Yellow -ForegroundColor Black
            }
        Else {
            Write-Host "Stat PC 1   - $PC1"
            }
        ''
        ''
        ''
        'City 2 Computers'
        '----------------'
        If ($PC2 -match 'Failed to ping') {
            Write-Host "Stat PC 2   - $PC2" -BackgroundColor Red -ForegroundColor White
            }
        ElseIf ($PC2 -match 'Needs to be be logged in') {
            Write-Host "Stat PC 2   - $PC2" -BackgroundColor Yellow -ForegroundColor Black
            }
        Else {
            Write-Host "Stat PC 2   - $PC2"
            }
    }

     

    0

  16. Iwan Dobrev 8 months ago

    Nothing works from here. What about the domain settings? I think to make this work it is required to enter the domain and to use the correct filters, because I'm sure that the command depends on how the domain configs are set.

    0

  17. Mohamed Eliwa 8 months ago

    Option 1
    (Get-CimInstance -ComputerName localhost -ClassName Win32_ComputerSystem).CimInstanceProperties | where{$_.Name -like "UserName"}

    Option 2
    (Get-CimInstance Win32_LoggedOnUser).Antecedent.Name | Select-Object -Unique

    +1

  18. Greg Pennings 6 months ago
    $ComputerName = "[name of computer]"
    $Domain = "[Domain]"
    	$SessionToLogOff = Invoke-Command -ComputerName $ComputerName -ScriptBlock {Get-Process -IncludeUserName | Select-Object UserName,SessionId | Where-Object {$PSItem.UserName -ne $null -and $PSItem.UserName.StartsWith($Domain) -and $PSItem.SessionId -ne "0"}} | sort sessionID -Unique | ogv -PassThru
    
    	if ($SessionToLogOff) {
        #Invoke-RDUserLogoff -HostServer $ComputerName -UnifiedSessionID (Invoke-Command -ComputerName $ComputerName -ScriptBlock {Get-Process -IncludeUserName | Select-Object UserName,SessionId | Where-Object {$PSItem.UserName -ne $null -and $PSItem.UserName.StartsWith($Domain) -and $PSItem.SessionId -ne "0"} -ErrorAction SilentlyContinue | Sort-Object SessionId -Unique} | Select-Object UserName,SessionId | OGV -PassThru).SessionId -Force
        Invoke-RDUserLogoff -HostServer $ComputerName -UnifiedSessionID $SessionToLogOff.SessionId -ErrorAction SilentlyContinue -Force
    		} #end if
    	else {
        Write-host "No users logged on $ComputerName" -ForegroundColor Green
    		} #end else
    0

  19. Alexander (Rank: 1)
    5 months ago

    Or even better to use this command:

    query user /server:$Server

    0

Leave a reply

Please enclose code in pre tags

Your email address will not be published. Required fields are marked *

*

© 4sysops 2006 - 2021

CONTACT US

Please ask IT administration questions in the forums. Any other messages are welcome.

Sending

Log in with your credentials

or    

Forgot your details?

Create Account