It’s 8:40 on a Monday. The help desk has three tickets saying “can’t log in, it just spins”. Nine times out of ten the cause is the same: a profile or app still holds on in a disconnected session from last Friday, and the new logon hangs until that session goes away. Before you can fix it, you need to know which host the session is on, what state it’s in, and whose it is. This guide covers the built-in commands, a small PowerShell loop for a farm of several hosts, and the point where a GUI console starts to pay for itself.
Use only on systems you administer and with your organisation’s authorization. Logging off or resetting a session ends that user’s work, so warn them first.
What you need before you start
- Rights. Microsoft’s docs say you need Full Control on the RDP listener to log off or reset another user’s session. Members of the local Administrators group on the session host have this by default. To just list sessions, the lighter “query information” permission is enough.
- Network path. The
query,logoffandresetcommands talk to the Terminal Services RPC interface on each host. On a domain network, the built-in Remote Administration firewall rule group usually covers this. - A list of hosts. In an RDS deployment you can get it from the Connection Broker. For a few standalone hosts, a text file works fine.
Step 1 — List sessions on one host
Start small. From an admin workstation or a management server:
quser /server:RDSH01
# same output, older name:
query session /server:RDSH01
quser shows the user name, session name (for example rdp-tcp#7), session ID, state (Active or Disc), idle time and logon time. Write down the ID, because every later command uses it. Session IDs only mean something on the host that issued them. Session 4 on RDSH01 has nothing to do with session 4 on RDSH02.
Step 2 — Sweep the whole farm with PowerShell
quser prints text for people to read, not objects, and disconnected sessions leave the session-name column empty. The regex below allows for that gap:
$hosts = Get-Content C:\Admin\rdsh-hosts.txt # one host name per line
$pattern = '^\s*>?(?<User>\S+)\s+(?:(?<Session>\S+)\s+)?(?<Id>\d+)\s+(?<State>\S+)\s+(?<Idle>\S+)\s+(?<Logon>.+?)\s*$'
$sessions = foreach ($h in $hosts) {
$lines = quser /server:$h 2>$null
if (-not $lines) { Write-Warning "$h returned nothing (no sessions or unreachable)"; continue }
foreach ($line in ($lines | Select-Object -Skip 1)) {
if ($line -match $pattern) {
[pscustomobject]@{
Host = $h
User = $Matches.User
Id = [int]$Matches.Id
State = $Matches.State
Idle = $Matches.Idle
LogonAt = $Matches.Logon
}
}
}
}
$sessions | Where-Object User -eq 'jdoe' | Format-Table -AutoSize
$sessions | Where-Object State -ne 'Active' | Sort-Object Host | Format-Table -AutoSize
Two caveats. First, the column text is localized: on a German or French server, “Disc” and “Active” appear in that language, so filter on what your hosts actually print. Second, the logon time is left as a string on purpose, because the date format follows each host’s regional settings.
If you run a full RDS deployment with a Connection Broker, the RemoteDesktop module gives you real objects and skips the parsing:
Import-Module RemoteDesktop
Get-RDUserSession -ConnectionBroker 'rdcb01.corp.example.com' |
Select-Object HostServer, UserName, UnifiedSessionId, SessionState, CreateTime |
Sort-Object HostServer
Step 3 — Warn the user
Microsoft’s own guidance for logoff is to message the user before you end the session. It takes five seconds and saves you an argument about lost spreadsheets:
msg 3 /server:RDSH01 /time:120 "IT: your session will be signed out in 2 minutes. Please save your work."
For a disconnected session, nobody will read the message. Check with the user or their manager instead, especially if they may have unsaved documents open in that session.
Step 4 — Log off or reset
Pick the gentlest option that works:
- Log off closes the user’s processes in order and deletes the session. Try this first.
logoff 3 /server:RDSH01 /v - Reset tears the session down without an orderly logoff. The docs reserve it for sessions that “malfunction or appear to have stopped responding”. Expect unsaved data to be lost.
reset session 3 /server:RDSH01 /v - In a broker deployment, use the cmdlet so the broker’s view stays consistent:
Invoke-RDUserLogoff -HostServer 'rdsh01.corp.example.com' -UnifiedSessionID 3 -Force
You can’t log off the console session with logoff. If the stuck session is the console, you’ll need a different approach, often a restart during a maintenance window.
Step 5 — Stop it from coming back
Stale disconnected sessions are a policy problem. In Group Policy, go to Computer Configuration → Administrative Templates → Windows Components → Remote Desktop Services → Remote Desktop Session Host → Session Time Limits. Set a limit for disconnected sessions (a few hours is a common choice), and an idle limit that fits how your users work. Run gpupdate on the hosts, then check the result with the sweep from step 2 a day later.
Common mistakes
- Running the loop from a non-elevated prompt and treating “no sessions” as the truth. An empty result can also mean access denied or a blocked RPC path. The warning in the script is there to catch this.
- Resetting when a logoff would have worked. A reset skips the profile unload, which can leave you with a temporary profile at the next logon, the very problem you were trying to fix.
- Mixing up session IDs across hosts when you copy commands from a sweep. Always include
/server:. - Using session tools to watch people. These commands exist to keep hosts healthy. Monitoring colleagues’ activity is a separate matter, and it needs policy, legal review and transparency.
When a console beats the commands
The loop above is enough for two or three hosts. Once you’re triaging sessions across a larger farm every day, a dedicated console saves real time. LizardSystems Terminal Services Manager shows sessions and processes across several hosts in one window. The vendor also lists messaging, disconnect, log off, reset and shadowing, and connects over SMB (TCP 445) and RPC, plus TCP 3389 for shadowing. Admin rights on each host are still required. If you’d rather stay with Microsoft’s free tooling, Windows Admin Center covers per-server tasks but has no farm-wide session list. We weigh the two in Terminal Services Manager vs Windows Admin Center.
For the history of who connected and when, as opposed to who is on right now, see pulling RDP logon history from event logs. More session tools are listed under RDS & Session Management, and where to get explains how to obtain any of them from the vendor and check the file before you run it.