- Mission: disconnect stale sessions from servers with the aid of a script
- Symptoms
- User connected to remote desktop and did not logoff after finish the job
- Session stays connected and consuming/wasting ressources
- For some servers you have multiple stale connected sessions and the ressources being wasted are considerably important
- Prerequisites
- Have Active Directory module installed
- Run the script with a credential with admin privileges to the target servers
Single server
From an elevated powershell run the following to query the connected session
qwinsta /server:[Server_Name]
You should have a result like this
Take note of the session number you want to disconnect.
You can see different states for the connections. When a state is tagged as Active probably there is someone connected and working on the server. If the state is being displayed as Disc the person connected to the server probably forgot to logoff and have clicked on close from the RDP header.
In this case you can run rwinsta which will force logoff for this Disc session
rwinsta /server:[Server_Name] [Session_Number]
For instance, if we want to disconnect the session ID 26 displayed on the image above
rwinsta /server:[Server_Name] 26
Multiple Servers
With the help of a powershell script you can easily force a logoff for Disc sessions
Firstly create a variable containing all the servers you wish to have the sessions disconnected. In the example I’ll get all computer objects on Active Directory that have Servers on its descriptions. But you can use a Get-Content and have a text file containing all the servers you want in it.
$Servers = Get-ADComputer -Filter * -Properties OperatingSystem | ? { $_.OperatingSystem -match "Server" } | Select -ExpandProperty Name
Then, inside a ForEach, the following commands will query the sessions, validate their states and disconnect those tagged with Disc
ForEach ($Server in $Servers) {
# Query for RDP sessions on the server
$c = qwinsta /server:$Server | where {$_.gettype().equals([string])}
# Convert qwinsta results on powershell readable data
$starters = New-Object psobject -Property @{"SessionName" = 0; "Username" = 0; "ID" = 0; "State" = 0; "Type" = 0; "Device" = 0;};
foreach($line in $c) {
if($line.trim().substring(0, $line.trim().indexof(" ")) -eq "SESSIONNAME") {
$starters.Username = $line.indexof("USERNAME");
$starters.ID = $line.indexof("ID");
$starters.State = $line.indexof("STATE");
$starters.Type = $line.indexof("TYPE");
$starters.Device = $line.indexof("DEVICE");
continue;
}
$o = New-Object psobject -Property @{
"ServerName" = $Server
;"SessionNAme" = $line.trim().substring(0, $line.trim().indexof(" ")).trim(">")
;"Username" = $line.Substring($starters.Username, $line.IndexOf(" ", $starters.Username) - $starters.Username)
;"ID" = $line.Substring($line.IndexOf(" ", $starters.Username), $starters.ID - $line.IndexOf(" ", $starters.Username) + 2).trim()
;"State" = $line.Substring($starters.State, $line.IndexOf(" ", $starters.State)-$starters.State).trim()
;"Type" = $line.Substring($starters.Type, $starters.Device - $starters.Type).trim()
;"Device" = $line.Substring($starters.Device).trim()
}
# Disconnect all sessions tagged as Disc
if($o.State -eq "Disc"){
if(!($o.Username -eq "")){
$Username = $o.Username
$ID = $o.ID
Write-Host " "$CountSession " - Server $Server - Closing $Username on session " $o.ID
rwinsta.exe $ID /server:$Server
$CountSession ++
}
}
}}
The full script
# Create a list (variable) containing all servers from Active Directory
$Servers = Get-ADComputer -Filter * -Properties OperatingSystem | ? { $_.OperatingSystem -match "Server" } | Select -ExpandProperty Name
# ForEach to be run against each server inside the variable $Servers
ForEach ($Server in $Servers) {
# Query for RDP sessions on the server
$c = qwinsta /server:$Server | where {$_.gettype().equals([string])}
# Convert qwinsta results on powershell readable data
$starters = New-Object psobject -Property @{"SessionName" = 0; "Username" = 0; "ID" = 0; "State" = 0; "Type" = 0; "Device" = 0;};
foreach($line in $c) {
if($line.trim().substring(0, $line.trim().indexof(" ")) -eq "SESSIONNAME") {
$starters.Username = $line.indexof("USERNAME");
$starters.ID = $line.indexof("ID");
$starters.State = $line.indexof("STATE");
$starters.Type = $line.indexof("TYPE");
$starters.Device = $line.indexof("DEVICE");
continue;
}
$o = New-Object psobject -Property @{
"ServerName" = $Server
;"SessionNAme" = $line.trim().substring(0, $line.trim().indexof(" ")).trim(">")
;"Username" = $line.Substring($starters.Username, $line.IndexOf(" ", $starters.Username) - $starters.Username)
;"ID" = $line.Substring($line.IndexOf(" ", $starters.Username), $starters.ID - $line.IndexOf(" ", $starters.Username) + 2).trim()
;"State" = $line.Substring($starters.State, $line.IndexOf(" ", $starters.State)-$starters.State).trim()
;"Type" = $line.Substring($starters.Type, $starters.Device - $starters.Type).trim()
;"Device" = $line.Substring($starters.Device).trim()
}
# Disconnect all sessions tagged as Disc
if($o.State -eq "Disc"){
if(!($o.Username -eq "")){
$Username = $o.Username
$ID = $o.ID
Write-Host " "$CountSession " - Server $Server - Closing $Username on session " $o.ID
rwinsta.exe $ID /server:$Server
$CountSession ++
}
}
}}