9

I am trying to get currently logged in users who has active session.

In the task manager, the list is crystal clear that there are two user sessions and one is active.

I want to query the same via Powershell. I tried few of the available commands Get-WmiObject Win32_LoggedOnUser | Select Antecedent -Unique which lists lot more users than I can even see [domain joined computer]

I am looking for the query which can give results exactly like Task Manager. enter image description here

RaceBase
  • 693

8 Answers8

4

Here's how I do it. It doesn't work for rdp sessions though.

$out = query session | where {$_ -match 'console'}
$array = $out -split('\s+')
$consoleuser = $array[1]

Or:

$consoleuser = query session | select-string console | foreach { -split $_ } | 
  select -index 1
js2010
  • 713
3

The issue with Get-WmiObject Win32_LoggedOnUser | Select Antecedent -Unique is that it shows all sessions even those that have been closed since the last time the computer rebooted. The easiest way to poll sessions is unfortunately using the old executable query.exe.

You can convert the output of query.exe to objects using a bit of regex:

$Computer = $env:COMPUTERNAME
$Users = query user /server:$Computer 2>&1

$Users = $Users | ForEach-Object {
    (($_.trim() -replace ">" -replace "(?m)^([A-Za-z0-9]{3,})\s+(\d{1,2}\s+\w+)", '$1  none  $2' -replace "\s{2,}", "," -replace "none", $null))
} | ConvertFrom-Csv

foreach ($User in $Users)
{
    [PSCustomObject]@{
        ComputerName = $Computer
        Username = $User.USERNAME
        SessionState = $User.STATE.Replace("Disc", "Disconnected")
        SessionType = $($User.SESSIONNAME -Replace '#', '' -Replace "[0-9]+", "")
    } 
}

Which will give you output like this:

ComputerName Username SessionState SessionType
------------ -------- ------------ -----------
BSMITH-LT    bobsm    Active       console    

Taking it a lot further in to a function:

function Convert-QueryToObjects
{
    [CmdletBinding()]
    [Alias('QueryToObject')]
    [OutputType([PSCustomObject])]
    param
    (
        [Parameter(Mandatory = $false,
                   ValueFromPipeline = $true,
                   ValueFromPipelineByPropertyName = $true,
                   Position = 0)]
        [Alias('ComputerName', 'Computer')]
        [string]
        $Name = $env:COMPUTERNAME
    )

    Process
    {
        Write-Verbose "Running query.exe against $Name."
        $Users = query user /server:$Name 2>&1

        if ($Users -like "*No User exists*")
        {
            # Handle no user's found returned from query.
            # Returned: 'No User exists for *'
            Write-Error "There were no users found on $Name : $Users"
            Write-Verbose "There were no users found on $Name."
        }
        elseif ($Users -like "*Error*")
        {
            # Handle errored returned by query.
            # Returned: 'Error ...<message>...'
            Write-Error "There was an error running query against $Name : $Users"
            Write-Verbose "There was an error running query against $Name."
        }
        elseif ($Users -eq $null -and $ErrorActionPreference -eq 'SilentlyContinue')
        {
            # Handdle null output called by -ErrorAction.
            Write-Verbose "Error action has supressed output from query.exe. Results were null."
        }
        else
        {
            Write-Verbose "Users found on $Name. Converting output from text."

            # Conversion logic. Handles the fact that the sessionname column may be populated or not.
            $Users = $Users | ForEach-Object {
                (($_.trim() -replace ">" -replace "(?m)^([A-Za-z0-9]{3,})\s+(\d{1,2}\s+\w+)", '$1  none  $2' -replace "\s{2,}", "," -replace "none", $null))
            } | ConvertFrom-Csv

            Write-Verbose "Generating output for $($Users.Count) users connected to $Name."

            # Output objects.
            foreach ($User in $Users)
            {
                Write-Verbose $User
                if ($VerbosePreference -eq 'Continue')
                {
                    # Add '| Out-Host' if -Verbose is tripped.
                    [PSCustomObject]@{
                        ComputerName = $Name
                        Username = $User.USERNAME
                        SessionState = $User.STATE.Replace("Disc", "Disconnected")
                        SessionType = $($User.SESSIONNAME -Replace '#', '' -Replace "[0-9]+", "")
                    } | Out-Host
                }
                else
                {
                    # Standard output.
                    [PSCustomObject]@{
                        ComputerName = $Name
                        Username = $User.USERNAME
                        SessionState = $User.STATE.Replace("Disc", "Disconnected")
                        SessionType = $($User.SESSIONNAME -Replace '#', '' -Replace "[0-9]+", "")
                    }
                }
            }
        }
    }
}

and now you can do things like: Get-ADComputer -Filter {Name -like "SERVER*"} | Convert-QueryToObjects | ? {$_.SessionState -eq 'Active'}

omniomi
  • 86
3
Get-WmiObject Win32_Process -f 'Name="explorer.exe"'  |%  getowner  |% user
alex
  • 51
2

This can be done with:

get-wmiobject -Class Win32_Computersystem | select Username
s0mm3r
  • 31
1

A little late to the party, but this is how I do it. I get the output from quser, replace tabs with commas and then convert the data from CSV to an object.

$QUserToRichObject = ((Invoke-Expression quser) -replace '\s{2,}', ',' | ConvertFrom-Csv)

If($QUserToRichObject){

    $UserSessions = @()

    ForEach($Record in $QUserToRichObject){

        # If the active session, remove the '>' character from Username value
        If($Record.USERNAME -Like ">*"){$Record.USERNAME = ($Record.USERNAME -Replace ">", "")}

        $UserSessions += @{
            Username        = [string]$Record.USERNAME
            SessionName     = [string]$Record.SESSIONNAME
            ID              = [string]$Record.ID
            State           = [string]$Record.STATE
            Idle            = [string]$Record.'IDLE TIME'
            LogonTime       = [string]$Record.'LOGON TIME'
        }
    }
}

$UserSessions

<#
Outputs:
Name                           Value                                                                                                                                                                           
----                           -----                                                                                                                                                                           
ID                             1                                                                                                                                                                               
SessionName                    console                                                                                                                                                                         
Idle                           none                                                                                                                                                                            
LogonTime                      04/11/2019 14:01                                                                                                                                                                
Username                       SomeUsername                                                                                                                                                                 
State                          Active
#>
Arbiter
  • 183
1

Expanding on alex's interesting solution; this is a more "PowerShell native" way to do it (though WMI and CIM are native in a way).

# This will get unique users that have any process running on the computer
Get-process -IncludeUserName | Select-Object UserName -Unique

This solution will include all system accounts too that are running processes on the computer, but that is fairly east to filter out if necessary.

I have verified that this also gets users connected with RDP on a Windows Server core instance too (which will not have an explorer shell running usually). Mind that it will also get users having for example an active PowerShell remote session too since that also creates a process for the user. Though depending on the use case, this might be useful too.

1

A small addition to the answers that are using the quser / query user approach.

To make the output locale independent, include a code page change to the command line:

Invoke-Expression 'cmd /c "chcp 437 > nul: & query user"'

It's also worth noting that if you have to deal with the locale language, you may also have to deal with the date and time format.

In this case, you can use the ParseExact method from the System.DateTime class. In the example below the $LogonTime variable is assumed to contain the respective LOGON TYPE string from the quser / query user output:

$ShortDate = [System.Threading.Thread]::CurrentThread.CurrentCulture.DateTimeFormat.ShortDatePattern;
$ShortTime = [System.Threading.Thread]::CurrentThread.CurrentCulture.DateTimeFormat.ShortTimePattern;

[datetime]::ParseExact($LogonTime, "$ShortDate $ShortTime", [culturalinfo]::InvariantCulture);

0

Given that the query and quser commands are not natively present on "Home" versions of Windows, the Powershell answers above should be upvoted as more universal. Here's a one-line command that works on all editions of Windows 10/11 and pulls out just the full username of the current user:

$myuser = get-wmiobject -Class Win32_Computersystem | foreach { $_.UserName };echo $myuser.split('\')[1]