Заметки · 26.06.2023

Logoff на терминальной ферме

Чуть раньше (вот здесь) я рассказывал, как произвести выход пользователей на терминальной ферме по признаку запущенного проводника. Конечно, с точки зрения служб удалённых рабочих столов этот метод не информативен неверен, но он хорош, если подменить explorer.exe на что-то другое и завершить сессии пользователей, у которых запущенно какое-то конкретное приложение. Больше примеров придумать не могу. Вечер. Что поделать.

Зато сегодня расскажу про грамотный и более гибкий метод завершения сессий пользователей на терминальной ферме. Функционал этого метода гвоздями прибит к модулю PSTerminalServices (здесь гит) и без этого кудесника ничего работать не будет.

Выкладывать скрипт отдельно я не буду. Будем собирать по кусочкам, а точнее по блокам. Конечно, прежде всего, нам необходим блок с объявлением переменных, в роли которых у нас массив серверов (которые можно смело менять на свои) и имя того самого волшебного модуля вот для этой функции.

# Блок переменных # 
$module = "PSTerminalServices"
$servers = @("RDS01", "RDS02", "RDS03")

Далее идёт блок функций. В нём будет задействована только одна — уже упомянутая выше — функция, а дальше попробуем импортировать в текущую сессию или же установить (а уже потом импортировать) модуль PSTerminalServices.

# Блок функций #
function ModuleAvailable($ModuleName)
{
    if (Get-Module -ListAvailable -Name $ModuleName)
    { return $True } 
    else { return $False }
}

# Импорт/установка модуля #
$available = ModuleAvailable -ModuleName $module
if ($available) { Import-Module $module -Force }
else { 
       Install-Module $module -Scope AllUsers
       Import-Module $module -Force
     }

Теперь началось самое интересное — циклы. Всего я подготовил два перебора: завершение отключённых сессий на терминальной ферме и завершение вообще всех сессий. Последний цикл полезно запускать или в случаях, когда «ваще коллапс«, или в ночное время суток.

# Завершение отключенных сессий #
foreach($server in $servers)
{  
  $disabled = Get-TSSession -ComputerName $server | Where-Object { $PSItem.ConnectionState -eq "Disconnected" -and $PSItem.UserName -ne "" }
  $disabled | Stop-TSSession -Force
}

К примеру, здесь цикл отбирает на каждом узле сеансов только те сессии, которые (сейчас внимание!) не содержат пустого имени пользователя и пылятся в отключённом статусе.

Соответственно, чтобы погасить вообще всё (а потом закрыть дверь и уйти) — делаем выборку, с добавлением сеансов в активном статусе:

# Завершение всех сессий #
foreach($server in $servers)
{  
  $allsessions = Get-TSSession -ComputerName $server | Where-Object { $PSItem.ConnectionState -eq "Active" -and $PSItem.ConnectionState -eq "Disconnected" -and $PSItem.UserName -ne "" }
  $allsessions | Stop-TSSession -Force
}

И, наконец, итоговый вариант будет выглядеть вот так:

# Блок переменных # 
$module = "PSTerminalServices"
$servers = @("RDS01", "RDS02", "RDS03")

# Блок функций #
function ModuleAvailable($ModuleName)
{
    if (Get-Module -ListAvailable -Name $ModuleName)
    { return $True } 
    else { return $False }
}

# Импорт/установка модуля #
$available = ModuleAvailable -ModuleName $module
if ($available) { Import-Module $module -Force }
else { 
       Install-Module $module -Scope AllUsers
       Import-Module $module -Force
     }

# ТЕПЕРЬ ОСТАВЬТЕ КАКОЙ-ТО ОДИН ЦИКЛ #
# Либо завершение отключенных сессий #
foreach($server in $servers)
{  
  $disabled = Get-TSSession -ComputerName $server | Where-Object { $PSItem.ConnectionState -eq "Disconnected" -and $PSItem.UserName -ne "" }
  $disabled | Stop-TSSession -Force
}

# Либо завершение всех сессий #
foreach($server in $servers)
{  
  $allsessions = Get-TSSession -ComputerName $server | Where-Object { $PSItem.ConnectionState -eq "Active" -and $PSItem.ConnectionState -eq "Disconnected" -and $PSItem.UserName -ne "" }
  $allsessions | Stop-TSSession -Force
}

Самый важный момент поста! Не забудьте в своём итоговом скрипте определиться с блоками циклов и оставить что-то одно, в противном случае (если бездумно скопипастить) сначала упадут все отключённые, а потом и все активные сессии пользователей. Если что — я предупреждал.