Search code examples
powershellremote-desktopterminal-services

How can I kick out the longest inactive user from a remote machine using powershell 1?


I need to find an easy way to kick off teammates from a server if they forget to log out. Our terminal service has a limit of 2 connections. I wrote the following function to get the logged in users. As I understand in powershell 2 there are nicer ways to do this but I am stuck with version 1 for now.

The following function will get me the logins per server. (If you know a prettier way to do this feel free to educate me. :) I am not happy with the func and hoping learn a few tricks from you.)

function get-terminal-users($serverName)
{
    $path = "C:\ntui\source\i386"

    cd -path $path

    $result = invoke-expression -Command ".\quser.exe /SERVER:$serverName" | select -Skip 1       

    $a = @()

    foreach($row in $result)
    {        
        $d = new-object Object
        $columns = $row.Replace("  ", " ").Split(" ") | ? {$_ -ne ""}    

        $d | add-member -membertype noteproperty -name UserName -value $columns[0]    
        $id = -1
        $sessionName = ""                                  
        $columShift = 0

        #this part was not ok and needed fixing. Thanks Winfred for the answer!
        if([system.int32]::tryparse($columns[1].ToString(),[ref] $id))
        {     
            $columShift --              
        }
        else
        {            
            $sessionName =  $columns[1]
            $id = [system.int32]::parse($columns[2].ToString())                                             
        }          

        $time = [DateTime]::Parse($columns[5 + $columShift] + " " + $columns[6 + $columShift])

        $d | add-member -membertype noteproperty -name SessionName -value $sessionName
        $d | add-member -membertype noteproperty -name ID -value $id     
        $d | add-member -membertype noteproperty -name State -value $columns[3 + $columShift]
        $d | add-member -membertype noteproperty -name Idle -value $columns[4 + $columShift]
        $d | add-member -membertype noteproperty -name Time -value $time 

        $a += $d
    }

    return $a
}

So I need the list if users and then I have to kick one guy out if the connection count is higher than the allowed maximum. If you know a way to get this info from the server I would really appreciate your help.

$name = "myserver"
$maxConnections = 2

$users = get-terminal-users($name)   

if($users.Count -eq $maxConnections)
{
    $idList = $users | ? { $_.State -eq "Disc" } | select -First 1 | foreach { $_.ID }   

    foreach($id in $idList)
    {          
        invoke-expression -Command ".\logoff.exe /SERVER:$name $id -V"                  
    }
}

The issue is that I can only interact with Active connections. Abandoned connections are still use up the available connections and I don't seem to be able to do much about them. A user with a Disc status seems to use up a terminal connection and when I call logoff on its id for some reason a local process is terminated. Is there a way to get rid of Disc connections?

rwinsta.exe also seems to only work with active connections.

I am running the script on XP and targeting a Win 2003 server.


Solution

  • You add the $id incorrectly to the custom object at this line:

    $d | add-member -membertype noteproperty -name ID -value $id
    

    Disconnected sessions don't have a session name see:

    quser.exe /SERVER:mycomputer
    USERNAME              SESSIONNAME        ID  STATE   IDLE TIME  LOGON TIME
    winfred                                   2  Disc      4+21:05  2/28/2012 8:38 AM
    

    And require the $columShift, which you have on all your properties, except on the ID Noteproperty. Because of that, you specify the ID incorrectly to logoff.exe, when you are trying to log off a disconnected session.

    Instead that line should be:

    $d | add-member -membertype noteproperty -name ID -value $columns[2 + $columShift]