List RDP Sessions on Remote Servers in PowerShell

Posted On 2016-02-17 by dwirch
Keywords:
Tags: Powershell Scripting Windows Windows Server 2008 Windows Server 2012
Views: 9712


You can use this handy little script to find remote desktop sessions on all servers running in your Active Directory domain.

It's a fact of life as a sysadmin.  You RDP to a machine, leave a task running, and disconnect. Invariably, you forget that session, and it sits their disconnected. Being a good admin, there is a password policy that requires you to change your password every 90 days.  The time comes, and you change your password, as required.

Bam. Your account is locked out. Now you get to go find all those abandoned sessions, running under your old credentials.  Good times.  Wouldn't it be handy if you could simply run a script to find all your dead sessions?

We've all used the query session command to get a list of sessions on a remote server, then use the logoff command to remotely log off any dead sessions.  If you haven't you can read up on it here: Killing Disconnected Terminal Server Sessions from the Command Line.

The general flow of the script is thus:

  • Import the Active Directory module (required), and set some variables.
  • Get a list of servers from Active Directory. I've included three methods of getting a list of machines in the script (two from AD, one from a file) within the script.
  • Iterate through the list with a ForEach loop, running qwinsta.exe against each host. This produces the same output as the query session command.
  • Parse the output of the qwinsta command.  In this script, the data seems to change places (my opinion, anyway), hence the hodgepodge of logical ANDs and whatnot.
  • Present the output to the user.

I've pasted the script below, and I hope someone gets some use out of this. As always, if you have any questions, comments, or concerns regarding this, drop a note in the comments, or start a thread in the forums. It'd be super cool if someone could improve upon this!

#
# import the active directory module
#

import-module activedirectory

#
# set some variables
#

$Today=Get-Date
$SessionList="`n`nRDP Session List - " + $Today + "`n`n"
$CurrentSN=0

#
# Get a list of servers from Active Directory. Note that two different strings have been
# given. The first one will get all servers in Active Directory, while the second one
# (which is commented) will target all hosts in a particular OU.  The third option is
# for grabbing a list of machines (one per line) from a file.
#

write-progress -activity "Getting list of servers from Active Directory" -status "... please wait ..."

$Servers=get-adcomputer -filter {OperatingSystem -like "*server*"}
# $Servers=Get-adcomputer -filter * -searchbase "OU=servers,dc=dwlab02,dc=local"
# $Servers=Get-Content "c:\files\myfile.txt"

$NumberOfServers=$Servers.Count

#
# Iterate through the retrieved list to check RDP sessions on each machine
#

ForEach ($Server in $Servers) {

    $ServerName=$Server.Name
    Write-progress -activity "Checking RDP Sessions" -status "Querying $ServerName" -percentcomplete (($CurrentSN/$NumberOfServers)*100)

    #
    # Run qwinsta and grab the output
    #

    try
    {
        $queryResults = (qwinsta /server:$ServerName | foreach { (($_.trim() -replace "\s+",","))} | convertfrom-csv)

        #
        # get session info from the instance
        #

        ForEach($QueryResult in $QueryResults) {
            
            $RDPUser=$QueryResult.USERNAME
            $SessionType=$QueryResult.SESSIONNAME
            $SessionID=$QueryResult.ID
            $ReturnedCurrentState=$QueryResult.State

            If($ReturnedCurrentState -eq $null){ $CurrentState="Disconnected" } Else { $CurrentState="Active" }
            
            #
            # filter out the chaff
            #

            If (($RDPUser -ne $NULL) -and ($SessionType -ne "console") -and ($SessionType -ne "services") -and ($SessionType -ne "rdp=tcp") -and ($RDPUser -ne "65536")) {
                $SessionList=$SessionList + "`n" + $ServerName + " logged in by " + $RDPUser + " on " + $SessionType + ", session id $SessionID $CurrentState"
            }
        }

    }
    catch
    {
        $SessionList=$SessionList + "`n Unable to query " + $ServerName
        write-host "Unable to query $ServerName!" -foregroundcolor Red
    }
    
    $CurrentSN++
}


# Send the output the screen.


$SessionList + "`n`n"


About the Author

has posted a total of 190 articles.


Comments On This Post

By: dwirch
Date: 2016-04-27

Watch out for line wrap in the script above!!

By: AnonymousCoward
Date: 2017-10-05

Hello.

In order to get the try catch to work i needed to add ErrorAction

       $queryResults = (qwinsta /server:$ServerName | foreach { (($_.trim() -replace "\s+",","))} | convertfrom-csv -ErrorAction Stop)

Also is using computers from a txt file remove the .name

         $ServerName=$Server

By: AnonymousCoward
Date: 2018-04-30

I notice when running this with PowerShell ISE (as administrator) on Windows Server 2012 R2, and calling the server list via a CSV import like so: 

$Servers = Import-CSV “Drive:\Filepatch\File_Name.csv”

That it runs without error, but then writes output that doesn't identify which machine the session is logged onto, making the output fairly useless to me. As an example, the output is a long series of items like this: 

 logged in by 2247 on username_redacted, session id Disc Disconnected
 logged in by username_redacted on rdp-tcp#16, session id 2249 Active
 logged in by username_redacted on >rdp-tcp#19, session id 2250 Active
 logged in by username_redacted on rdp-tcp#135, session id 2251 Active
 logged in by 2252 on username_redacted, session id Disc Disconnected

If you notice, it seems that the output variables are showing up in weird order for some lines. Notice the first line of the output shows "logged in by 2247" (which appears to be the session ID), "on username_redacted", while the second line shows "logged in by username_redacted", "on rdp-tcp#16, session ID 2249". 

I could ignore that part as long as I could get an idea of which servers had sessions on them, but the output doesn't mention the servername at all.

As a relative newb to all of this, I don't have much confidence in being able to spot the cause on my own, but looking at this part: 

ForEach($QueryResult in $QueryResults) {

            $RDPUser=$QueryResult.USERNAME
            $SessionType=$QueryResult.SESSIONNAME
            $SessionID=$QueryResult.ID
            $ReturnedCurrentState=$QueryResult.State

I see username, sessionname, result ID, and result state, but I don't see a server name variable there. 

Any help at all would be appreciated. 

By: dwirch
Date: 2018-05-01

$ServerName holds the name of the server, but I am not sure why your CSV is not working.  Maybe there are multiple values in there? Have you tried using just a straight text file, with one machine name per line?


Do you have a thought relating to this post? You can post your comment here. If you have an unrelated question, you can use the Q&A section to ask it.

Or you can drop a note to the administrators if you're not sure where you should post.


Your IP address is:54.166.212.152

Before you can post, you need to prove you are human. If you log in, this test goes away.


Code Links