Disconnect or log off idle sessions

Version: 1.5.7
Creator Name: Guy Leech
Date Created: 2018-10-13
Date Modified: 2018-11-23
Scripting language: PS
Download Count: 167

Disconnect or logoff sessions on the selected computer(s) which have been idle in excess of a given period, specified in minutes. Arguments: Idle Period - the time in minutes after which a disconnected session will be logged off or disconnected depending on the value for the "Logoff" argument (default is 30 minutes) Logoff - if true, sessions idle in excess of the idle period will be logged off, otherwise they will be disconnected (default is false so sessions will be disconnected)
Tags: Citrix,Log off,Idle Sessions,Disconnected Sessions

The Script

<#
    1) Find sessions idle over a given period and disconnect/logoff

    or

    2) Logoff all disconnected sessions over a given idle period or all if idle period is 0

    @guyrleech 2018
#>

[bool]$logoffDisconnected = $false ## set to false to perform 1) or true to perform 2)

[int]$idleOverMinutes = $args[0]
[bool]$logoff = $false
[int]$totalSessions = 0
[string]$qualifier = $null

if( $args.Count -ge 2 -and $args[1] -and $args[1] -eq 'True' )
{
    $logoff = $true
}

[array]$idleSessions = @( (quser.exe | select -Skip 1).Trim() | ForEach-Object `
{
    $totalSessions++
    [string[]]$fields = $_ -split '\s+'

    ## Headings are:  USERNAME              SESSIONNAME        ID  STATE   IDLE TIME  LOGON TIME

    ## if logon time has AM/PM indicator as opposed to 24 hour then there will be one more field
    [int]$disconnectedFieldCount = if( $_ -match '\s[AP]M$' ) { 7 } else { 6 }
    [int]$fieldOffset = if( $fields.Count -eq $disconnectedFieldCount ) { 0 } else { 1 } ## so we can skip passed sessionname since missing if disconnected
    [int]$idleDurationMinutes = `
        if( $fields[ 3 + $fieldOffset ] -eq '.' )
        {
            if( $fieldOffset )
            {
                -1
            }
            else ## disconnected
            {
                0
            }
        }
        elseif( $fields[3 + $fieldOffset ] -match '^(\d+)\+(\d+):(\d+)$' ) ## 33+22:16
        {
            ( [int]$Matches[1] * 24 + [int]$Matches[ 2 ] ) * 60 + [int]$Matches[ 3 ]
        }
        elseif( $fields[3 + $fieldOffset ] -match '^(\d+):(\d+)$' ) ## 19:35
        {
            [int]$Matches[1] * 60 + [int]$Matches[ 2 ]
        }
        else
        {
            [int]$fields[3 + $fieldOffset ]
        }

        if( $logoffDisconnected -and $fieldOffset )
        {
            ## Don't add to list as isn't disconnected and we are in the mode when only looking for disconnected sessions
        }
        elseif( $idleDurationMinutes -ge 0 -and $idleDurationMinutes -ge $idleOverMinutes )
        {
            [pscustomobject][ordered]@{ 
                'Username' = $fields[0]
                'Session Id' = $fields[1 + $fieldOffset]
                'Logon Time' = if( $disconnectedFieldCount -eq 7 ) { ( '{0} {1} {2}' -f $fields[-3] , $fields[-2] , $fields[-1] ) } else { ( '{0} {1}' -f $fields[-2] , $fields[-1] ) }
                'Idle Minutes' = $idleDurationMinutes
                'Connected' = $fieldOffset
        }
    }
} )

if( $logoffDisconnected )
{
    $logoff = $true
    $qualifier = ' disconnected and'
}

"Found $($idleSessions.Count) sessions$qualifier idle over $idleOverMinutes minutes out of $totalSessions sessions total"

$idleSessions | Sort 'Idle Minutes' -Descending | Format-Table -AutoSize

$affectedUsers = New-Object System.Collections.ArrayList

ForEach( $session in $idleSessions )
{
    [string]$exe = $null
    if( $logoff )
    {
        $exe = 'logoff.exe'
    }
    elseif( $session.Connected ) ## disconnect
    {
        $exe = 'tsdiscon.exe'
    }
    if( $exe )
    {
        $process = Start-Process -FilePath $exe -ArgumentList "$($session.'Session Id')" -PassThru -Wait -WindowStyle Hidden
        if( ! $process -or $process.ExitCode )
        {
            Write-Warning "Failed to run $exe successfully for session id $($session.'Session Id') user $($session.UserName)"
        }
        else
        {
            [void]$affectedUsers.Add( $session.Username )
        }
    }
}

Write-Output ( "{0} {1} sessions:" -f $(if( $logoff ) { 'Logged off' } else { 'Disconnected' }) , $affectedUsers.Count )

$affectedUsers | Sort -Unique | ForEach-Object { "`t$_" }