How do I script the logoff of disconnected RDP sessions?

There’s a (relatively) well known Group Policy that is designed to logoff idle/disconnected RDP sessions from terminal servers.  In reality, I’ve found it very hit and miss, and have now resorted to running a custom Scheduled Task to accomplish the same goal.

This script is designed to logoff any Disconnected session (on the machine where the script is run from) that has been idle for more than 1 hour.  I’ve deliberately left the option open to easily modify this to any preferred idle timeout, as you’ll see below with the if/elseif sections. This could be tidied up into a single ‘if’ block when you know what setting you’re using.

Note – you could also run this remotely (Windows Firewall state permitting) by changing the $server variable to any remote server.

 

Update 19/06/2019

In some cases the Session Name value/column is missing from the returned session information (and only on certain lines).  I’ve added a check for the number of values in each line ($line.length) to account for the missing data.  Both of the below scripts have been updated.

 

Update 27/05/19

A recent comment asked if you could check for a specific process (Notepad) first, before logging someone off.  That is, if Notepad was running, don’t log them off.

Here’s the exact same script as above, with the extra checks added (you can easily modify this to check for any other process).  Again, change the value of $server to whatever server you’re checking (local, or remote).

There is some duplication of code here that could be tidied up as well.

 

26 Comments How do I script the logoff of disconnected RDP sessions?

  1. joe

    Hi Kamal, Is this a powershell script or a vbs script? Also, if I by remote server do you mean the actual server name? Is there a way to run this remotely to multiple servers, instead of having to specify each individual server?

    Thanks,
    Joe

    Reply
    1. Kamal

      Yes – you could start with any array of server names – generated from a get-adcomputer command, or maybe pulling in from a text file – lots of options.
      Once you’ve got your server names into an array of strings (eg: $servers), then wrap the above in:
      foreach ($server in $servers) {
      #use $server in place of localhost
      }

      Reply
  2. nanjunda

    hi kamal.

    In this script i want one small change as administrator only should not logoff like that need make, can you please help me?

    Reply
    1. Kamal

      It’s a little more complicated, but not too hard.
      In the script, $line is split into different strings – one of those will be the username (specifically $line[1]).
      You could use get-aduser $line[1] – and then check its memberof property to see if it’s in an Admin group. If it is, ignore it, otherwise logoff the session.

      Reply
  3. Tom

    Hello Kamal,
    How do I modify the script to log off users that have been in disconnected state for greater than 10 minutes?

    Reply
    1. Kamal

      I would run the script as a Scheduled Task more frequently to check the idle time. If you wanted to be very specific about the 10 minutes rule, maybe run it every minute (but that seems extreme). Up to you.

      Change this:

      $idletime = $line[4];

      To this:

      $idletime = [int]$line[4];

      (That’ll typecast the idle time as an integer.)

      Then change this:

      # Check if idle for more than 1 day (has a '+') and log off
      if ($idletime -like "*+*") {

      To this:

      # Check if idle for more than 9 minutes and log off
      if ($idletime -gt 9) {

      Reply
    1. Kamal

      You would need to run the script at least once per hour (maybe every 30 minutes, just to be on the safe side), and change this line:

      if ($idletime -like "*+*") {

      To this:

      if ($idletime -like "*8:*") {

      Reply
  4. Mark

    Hi Kamal, was getting errors on this when I noticed that line 20 needs a closing double-quote for “*+*” in order to run the script without error. Brilliantly simple and works like a charm otherwise. Thanks for posting this and saving us time!

    Reply
  5. Rena

    Hi Kamal,

    The main Admin account has to stay connected and it is always ID 2

    How would (if possible) only log off certain ID’s or just not ID 2?

    Reply
    1. Kamal

      Yeah, it’s no problem at all. The ID is stored in $sessionid, so just wrap an if statement around the logoff command.

      Example:

      if ($sessionid -ne "2") {
      logoff $sessionid /server:"localhost" /v
      }

      Reply
      1. Rena

        The wraping works perfectly, I’m running into an issue with ID 3 being ignored for this ruleset (when I don’t want it to), currently that ID is idles for 1hr 41min

        # Check if idle for more than 1 hour (has a ‘:’) and log off
        } elseif ($idletime -like “*:*”) {
        if ($sessionid -ne “2”) {
        logoff $sessionid /server:”localhost” /v

        Reply
        1. Kamal

          Hard to say without running further tests on your server. Could be permissions related? What/who is Session 3?
          I would jump on the server and try running the logoff command manually from a command prompt and see what happens (error messages etc)

          Reply
          1. Rena

            Session 3 is just a standard user, I can log them off manually with no issues (through cmd or task mgr).

            The only difference between that user and others is that they are on a Mac (work program is windows only, Art Dept are all on Mac’s so they remote to use the program from server, out of office users use remote as well).

  6. Micke

    Hi Kamal! Great script! Do you know how to add an specific application as an exception for disconnecting the user – example if an disconnected user (could be away from keyboard) runs notepad.exe he/her wont be logged out? In this case we need to combine two values, not sure how. Thanks!

    Reply
        1. Mikael Hedlöf

          Tested this evening but unfortunately It wont work as it should. (skipped idle time). The issue is when fetching $state it seems like when looking at it with debug it fetches [STATE] [Active] and [1:22] – this is idle time. And my guesses here is that the disconnected user missing the SESSIONNAME, previous user has this therefore Active is displayed but not for the “Disc” user. Insted it skips that column. So Im thinking about another way to do it.. Right now I´m trying to use “function” Get-Session and query it out with trim and catch to try that way.

          //Newbee at PS 🙁

          Reply
          1. Mikael Hedlöf

            Next test worked 🙂 Needed to start with $state = $line[2] and when fetching $Sessionid ive used $sessionid = $line[1] – but will test this in bigger scales and depending how it looks on server I will udjust it. Thanks for nice site! 🙂

          2. Kamal

            Hi Mikael – good catch – I’ve now updated the script to account for missing SESSIONNAME values.

  7. Pouriya

    Hey Kamal,

    Is there a way to make a script to forcefully log off a user that his account is just disabled?

    Thanks in advance

    Reply
  8. Steph

    Hi Kamal,
    Excellent Script, Thank you!
    One question. When logging off disconnected users that have been idle longer than 2 hours elseif ($idletime -like “*2:*”).
    This only seems to catch disconnected sessions with 2 hours attached. Anything over 2 hours still stays disconnected.
    Any Ideas?

    Reply
    1. Kamal

      Thanks!

      Well – there are two approaches to the problem:

      – Run the script every 110 minutes (ie; more frequently than the time threshold you’re trying to adhere to) to always catch the “2”

      Or

      – Change the if statement (from the original post), like this:

      # If the session state is Disconnected
      if ($state -eq “Disc”) {

      # Check if idle more than 2 hours (is not ‘1:’ and not less than 1 hour) and log off
      if (($idletime -notlike “*1:*”) -and ($idletime -like “*:*”)) {

      logoff $sessionid /server:$server /v

      }
      }

      Reply

Leave a Reply to Kamal Cancel reply

Your email address will not be published. Required fields are marked *