PowerShell Script to Forcefully Disconnect All Active RDP Sessions on Windows Server


2 views

When administering Windows servers, you'll often need to forcibly disconnect Remote Desktop sessions - whether for maintenance, security reasons, or simply reclaiming system resources. While the graphical Terminal Services Manager works for one-off cases, automation requires command-line solutions.

Windows provides these built-in commands:

qwinsta - lists all sessions
rwinsta - resets a specific session

The challenge lies in parsing qwinsta's tabular output to extract session IDs for automated processing.

Here's a robust PowerShell function to handle this:

function Disconnect-RDPSessions {
    [CmdletBinding(SupportsShouldProcess=$true)]
    param(
        [Parameter()]
        [string]$ComputerName = $env:COMPUTERNAME
    )
    
    try {
        $sessions = qwinsta /server:$ComputerName | 
            Where-Object { $_ -notmatch '^ SESSIONNAME' } |
            ForEach-Object {
                $session = ($_ -split '\s+')
                [PSCustomObject]@{
                    SessionName = $session[1]
                    Username = $session[2]
                    ID = $session[3]
                    State = $session[4]
                    Type = $session[5]
                    Device = $session[6]
                }
            }
        
        $activeSessions = $sessions | Where-Object { $_.State -eq 'Active' }
        
        foreach ($session in $activeSessions) {
            if ($PSCmdlet.ShouldProcess("Session ID $($session.ID) ($($session.Username))", "Disconnect")) {
                rwinsta $session.ID /server:$ComputerName | Out-Null
            }
        }
        
        return $activeSessions.Count
    }
    catch {
        Write-Error "Failed to disconnect sessions: $_"
        return -1
    }
}

For applications requiring .NET integration:

using System;
using System.Diagnostics;

class RDPManager {
    static void Main(string[] args) {
        var startInfo = new ProcessStartInfo {
            FileName = "qwinsta",
            Arguments = "",
            RedirectStandardOutput = true,
            UseShellExecute = false
        };
        
        using (var process = Process.Start(startInfo)) {
            string output = process.StandardOutput.ReadToEnd();
            
            foreach (string line in output.Split('\n')) {
                if (line.Contains("Active")) {
                    string[] parts = line.Split(new[] { ' ' }, 
                        StringSplitOptions.RemoveEmptyEntries);
                    int sessionId = int.Parse(parts[2]);
                    
                    Process.Start("rwinsta", sessionId.ToString());
                }
            }
        }
    }
}

For legacy systems or quick scripts:

@echo off
setlocal enabledelayedexpansion

for /f "tokens=1-7 delims= " %%a in ('qwinsta ^| find "Active"') do (
    echo Disconnecting session ID: %%c
    rwinsta %%c
)
  • Administrator privileges are required
  • Some system sessions (like console) cannot be disconnected
  • User data may be lost when sessions are forcibly terminated
  • Consider warning logged-in users before mass disconnects

When administering Windows systems, you may need to programmatically disconnect active Remote Desktop sessions. Windows provides several built-in commands like qwinsta (query session) and rwinsta (reset session) that can help with this task. However, parsing their output can be challenging in scripts.

Here's a complete PowerShell script to disconnect all RDP sessions:


# Get all active sessions (excluding console and services)
$sessions = qwinsta | Where-Object { $_ -match 'rdp' -and $_ -notmatch 'services' -and $_ -notmatch 'console' }

# Extract session IDs
$sessionIds = $sessions | ForEach-Object { ($_ -split '\s+')[2] }

# Disconnect each session
foreach ($id in $sessionIds) {
    rwinsta $id
    Write-Host "Disconnected session ID: $id"
}

For those working with C#, here's how to accomplish the same task:


using System;
using System.Diagnostics;

class Program {
    static void Main() {
        Process process = new Process();
        process.StartInfo.FileName = "cmd.exe";
        process.StartInfo.Arguments = "/c qwinsta | find \"rdp\"";
        process.StartInfo.RedirectStandardOutput = true;
        process.StartInfo.UseShellExecute = false;
        process.Start();
        
        string output = process.StandardOutput.ReadToEnd();
        string[] lines = output.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
        
        foreach (string line in lines) {
            string[] parts = line.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
            if (parts.Length > 2) {
                string sessionId = parts[2];
                Process.Start("rwinsta", sessionId).WaitForExit();
                Console.WriteLine($"Disconnected session: {sessionId}");
            }
        }
    }
}

For simple batch scripting:


@echo off
for /f "tokens=2 delims= " %%a in ('qwinsta ^| find "rdp"') do (
    rwinsta %%a
    echo Disconnected session: %%a
)

When implementing these solutions, consider:

  • Running scripts with administrative privileges
  • Handling special cases like disconnected sessions
  • Logging actions for audit purposes
  • Adding error handling for robustness

You can also use WMI for more control:


Get-WmiObject -Class Win32_Process -Filter "Name='rdpclip.exe'" | ForEach-Object { $_.Terminate() }

Or the TSAdmin COM object:


$tsSessionMgr = New-Object -ComObject "Microsoft.TS.TSManager"
$tsSessionMgr.GetSessions() | Where-Object { $_.State -eq 0 } | ForEach-Object { $tsSessionMgr.DisconnectSession($_.SessionID) }