I have been using Quser for years and I was wondering how to parse the data out to Powershell. The best way to do this is to convert the output into a csv and then convert it from the csv to a psobject. Let us get started. First we output the quser to a variable.

$Users = Quser /server:"$($env:COMPUTERNAME)" 2> $null

In this example, we are going to grab the local computer’s logged-in information. Here is what the output looks like:

PS C:\Users\david> Quser /server:"$($env:COMPUTERNAME)" 2> $null
 USERNAME              SESSIONNAME        ID  STATE   IDLE TIME  LOGON TIME
 david                 console            11  Active       1:05  9/5/2021 9:29 PM
 susan                                    12  Disc         1:05  9/6/2021 11:14 AM

Next, we replace the first (^) > with nothing. It doesn’t look like much but it adds the spacing needed.

$Users = $Users -Replace '^>',''

Here is the output.

 USERNAME              SESSIONNAME        ID  STATE   IDLE TIME  LOGON TIME
 david                 console            11  Active       1:08  9/5/2021 9:29 PM
 susan                                    12  Disc         1:08  9/6/2021 11:14 AM

Now we will replace the tabs with commas.

$Users = $Users -Replace '\s{2,}',','
USERNAME,SESSIONNAME,ID,STATE,IDLE TIME,LOGON TIME
 david,console,11,Active,1:08,9/5/2021 9:29 PM
 susan,12,Disc,1:08,9/6/2021 11:14 AM

From here we want to go through each line and remove the last item if it has too many spaces. This happens when you run this command on a server. You will need to go through each line by line with a foreach loop.

$users = foreach ($User in $Users) {
    if ($User.Split(',').count -eq 5) {
        Write-Output ($User -replace '(^[^,]+)','$1,')
    } else {
        Write-Output $User
    }
}

Splitting the user by the comma, we need only 4 not 5. So we remove the first of the last object. If it is 4, we do nothing special and just reintroduce the original $user.

USERNAME,SESSIONNAME,ID,STATE,IDLE TIME,LOGON TIME
 david,console,11,Active,1:08,9/5/2021 9:29 PM
 susan,,12,Disc,1:08,9/6/2021 11:14 AM

Now we take that $user csv and convert it to a psobject using convertfrom-csv.

$users = $users | ConvertFrom-Csv
USERNAME    : david
SESSIONNAME : console
ID          : 11
STATE       : Active
IDLE TIME   : 1:08
LOGON TIME  : 9/5/2021 9:29 PM

USERNAME    : susan
SESSIONNAME :
ID          : 12
STATE       : Disc
IDLE TIME   : .
LOGON TIME  : 9/6/2021 11:14 AM

Next, we need to do some house cleaning. Sometimes the Idle time will show up as a dot. We don’t want that. We want it to show up as null. We do this by finding all the dot Idle times and setting that idle time to null with a foreach-object.

$users | where-object { $_."IDLE TIME" -like "." } | ForEach-Object { $_."IDLE TIME" = $null }
USERNAME    : david
SESSIONNAME : console
ID          : 11
STATE       : Active
IDLE TIME   : 1:08
LOGON TIME  : 9/5/2021 9:29 PM

USERNAME    : susan
SESSIONNAME :
ID          : 12
STATE       : Disc
IDLE TIME   :
LOGON TIME  : 9/6/2021 11:14 AM

Finally, we output the information by just putting the $users. That’s it yall. Lets put it together.

The Script

$Users = Quser /server:"$($env:COMPUTERNAME)" 2> $null
$Users = $Users -Replace '^>',''
$Users = $Users -Replace '\s{2,}',','
$users = foreach ($User in $Users) {
    if ($User.Split(',').count -eq 5) {
        Write-Output ($User -replace '(^[^,]+)','$1,')
    } else {
        Write-Output $User
    }
} 
$users = $users | ConvertFrom-Csv
$users | where-object { $_."IDLE TIME" -like "." } | ForEach-Object { $_."IDLE TIME" = $null }
$Users

Another Way

Another way to do this is to grab a process from the machine that everyone logged in would be using. Something like explorer. We do this with the get-ciminstance and the wmi object win32_process.

Get-ciminstance -ClassName win32_process -filter 'name ="explorer.exe"'
ProcessId Name         HandleCount WorkingSetSize VirtualSize
--------- ----         ----------- -------------- -----------
10576     explorer.exe 2805        142168064      2204137693184
18900     explorer.exe 2404        185618432      2204239441920

Then we pipe this information into Invoke-CimMethod and use the GetOwner method to grab the owner information.

Get-ciminstance -ClassName win32_process -filter 'name ="explorer.exe"' | Invoke-CimMethod -MethodName GetOwner

Domain          ReturnValue User  PSComputerName
------          ----------- ----  --------------
DESKTOP-XXXX0LB           0 david
                          2    

From here we grab the user information using a .user.

(Get-ciminstance -ClassName win32_process -filter 'name ="explorer.exe"' | Invoke-CimMethod -MethodName GetOwner).user