Scenario
In a Microsoft Remote Desktop environment, it’s a common need to force the logoff of a hanged user session. The NetEye Command Orchestrator (CMDO) can help us perform this task by executing remote commands through the Icinga2 agent API.
There’s a security limitation built into the Command Orchestrator which allows only numeric parameters for executed commands. But if we need to disconnect a user session and we only know the user account name, how can we bypass this limit?
The right approach
Each remote desktop user has a unique Session ID. This ID is unique within the Session Host which the user is connected to, but in a Remote Desktop farm consisting of several Session Host servers, the Session ID is not unique.
Usually, the Session Host server names are numbered, so it’s possible to extract the numeric part of the server’s name.
By concatenating the numeric part of the server’s name with the Session ID, we can build a unique number which identifies the user in the Remote Desktop farm. This number can be used to force kill the user session.
All the Remote Desktop runtime info is handled by the Connection Broker server: by using a PowerShell script we can collect the required data and also force a user session logoff.
PowerShell sample scripts
I’ve prepared two different PowerShell scripts to run on the Connection Broker server: one to get the list of disconnected users (we assume that a hanged user session will be in a disconnected state) followed by a computed unique number, and a second PowerShell script which takes that number as input and kills the user session.
Both scripts must be placed in a directory accessible by the Icinga 2 monitoring agent
(default path: C:\ProgramData\icinga2\etc\icinga2\scripts).
First script: getID.ps1
$disconnectedSessions = Get-RDUserSession -ConnectionBroker BrokerName | Where{$_.CollectionName -eq 'CollectionName' -and $_.SessionState -eq 'STATE_DISCONNECTED'} | Select-Object ServerName, SessionId, UserName | Sort-Object UserName
$output = @()
$a = @(foreach ($session in $disconnectedSessions){
$server=(($session.Servername).split(".")[0]).Substring(7) #example: server number is after 7 chars
$sessionID= $session.SessionId.ToString("00") #ensure that user session ID is always 2 chars
$PSObject = New-Object PSObject -Property @{
UserName = $session.UserName
ID = $server+$sessionID
}
$output += $PSObject
})
$output | Select-Object UserName, ID | Sort-Object UserName | ConvertTo-Html #html format required by CMDO
Second script: killID.ps1
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
$RDPID
)
$server = "name prefix"+ $RDPID.Substring(0,3)+".FQDN suffix" #rebuild the Session host server name
$UnifiedSessionID = ($RDPID.Substring($RDPID.Length-2)) #last two chars are the session ID
$RDSession = Get-RDUserSession | Where-Object -Filter {$_.HostServer -eq "$server" -and $_.UnifiedSessionId -eq "$UnifiedSessionID" }
if ($RDSession) {
$RDSession | Invoke-RDUserLogoff -Force
write-host "The user" $RDSession.UserName "is logged off from" $RDSession.HostServer "server"
}
else {
Write-host "The session " $UnifiedSessionID " was not found on the server " $server
}
NetEye integration
The right way to use the Remote User session kill function is through NetEye’s CMDO (Command Orchestrator). CMDO will first check that the user is authorized to execute these commands and then call the Icinga 2 API to perform the remote actions.
The CMDO Feature Module was released at the end of 2020
(see https://www.neteye-blog.com/2020/12/the-new-command-orchestrator-feature-module/). Please refer to the NetEye User Guide (IT Operations > Command Orchestrator) in order to define the remote CMDO commands to be executed on the Connection Broker server.
Sample output from the getID script:
The first 3 digits are the last characters of the Session Host server name, while the last 2 digits are the user session ID. The concatenated 5-digit ID is then the input parameter for the killID remote command.