Полезные скрипты для RDS фермы

Обновлено 16.08.2023

powershell scripts logo

Добрый день! Уважаемые читатели и гости IT блога Pyatilistnik.org. В минувший раз мы с вами разобрали, что такое современные центры обработки данных, как они работаю и как без них уже невозможно представить жизнь в цифровом мире. Сегодня я хочу в данной статье поделиться огромной и полезной подборкой скриптов PowerShell по обслуживанию RDS фермы. И мне так проще будет их искать и не затерять, да и вам что-то, да пригодится в своей работе, я всегда за то, чтобы помогать друг другу прокачивать знания, не люблю я жадин таких, кто не делится опытом. Так же жду от вас полезные скрипты и что вы ими решаете в комментариях, самые полезные добавлю в саму статью.

Как использовать данные скрипты

  1. ✅Во первых используйте для запуска скриптов PowerShell ISE в режиме администратора
  2. ✅Запускать скрипты нужно с правами учетной записи у которой есть права на конечные серверы, чаще всего это администратор домена или леса, но у меня для этого отдельная учетная запись с правами администратора на всю RDS ферму.
  3. ✅Лично я для удобства выполнения скриптов выполняю их на самом брокере,

Если вы еще до сих пор не знаете, что такое RDS ферма и как она помогает системным администраторам упрощать жизнь, то почитайте по ссылке. И если надумаете ее установить, то почитайте статью "Установка и настройка Remote Desktop Services High Availability на Windows Server 2022"

‼️Все Скрипты написал Иван Сёмин, сайт https://pyatilistnik.org/ использовать можно бесплатно, размещать на других ресурсах только при добавлении активной ссылки на источник‼️

Скрипт по переводу RDSH в режим стока

✅Первый скрипт который я опишу делает перевод серверов на которых работают пользователи в режим Drain Mode, чтобы в дальнейшем их обслужить без влияния на работу людей.

# Импортируем список серверов из файла
$servers = Get-Content "c:\Temp\termfarm.txt"

# Переводим или выводим из Drain Mode 
foreach ($server in $servers)

{

Set-RDSessionHost -SessionHost $server -NewConnectionAllowed yes -ConnectionBroker "rdcb01.root.pyatilistnik.org"

}

Скрипт был взят из статьи про режим стока, там все подробности.

Скрипт по поиску событий RDS для определенного логина

✅В задачу данного скрипта входит проанализировать три журнала RDS на ваших брокерах подключений, которые скрипт получает из скрипта:

  • Microsoft-Windows-TerminalServices-SessionBroker-Client/Operational
  • Microsoft-Windows-TerminalServices-SessionBroker/Operational
  • Microsoft-Windows-TerminalServices-RemoteConnectionManager/Operational

В них он будет искать события, где есть логин нужного пользователя, если они есть то выводит эти данные в CSV файл, который потом можно открыть через Notepad++ и получить всю выборку за последние 30 минут.

$Servers = Get-Content "C:\Temp\RDS\brokers.txt"
$EventLogs = @("Microsoft-Windows-TerminalServices-SessionBroker-Client/Operational", "Microsoft-Windows-TerminalServices-SessionBroker/Operational", "Microsoft-Windows-TerminalServices-RemoteConnectionManager/Operational")

foreach($Server in $Servers){
foreach($EventLog in $EventLogs){
$Events = Get-WinEvent -ComputerName $Server -LogName $EventLog -FilterXPath "*[System[TimeCreated[timediff(@SystemTime) <= '1800000']]]"
foreach($Event in $Events){
if($Event.Message -like "*ИМЯ_ПОЛЬЗОВАТЕЛЯ*"){
$Output = [PSCustomObject]@{
TimeCreated = $Event.TimeCreated
Source = $Event.ProviderName
Message = $Event.Message
}
$Output | Format-Table -AutoSize
$Output | export-csv "C:\Temp\RDS\out_ID.csv" -Append
}
}
}
}

Поиск по логам RDS

Массовая проверка отпечатка сертификата используемого при RDP сессии на удаленных серверах

✅Начиная с Windows 11 и Windows Server 2022 компания Microsoft усиливает безопасность по всем направлениям, и теперь если вам на удаленной стороне, куда вы производите RDP подключение, должен подставляться не самоподписанный сертификат, а доверенный. Если этого не сделать, то есть вероятность получать, что произошла внутренняя ошибка.

В идеале у вас должен быть поднят Active Directory CA, но если его нет, то вы легко можете установить любой Wildcard сертификат с вашим доменом (Даже внутренний если не хотите настраивать политику CA) от доверенного CA. Данный скрипт запрашивает удаленные серверы из файла и выводит какой sslCertificateHash, используется, чтобы понять тот сертификат или нет, что вы до этого ставили.

$serverList = Get-Content "C:\Temp\SSL\Servers.txt"

foreach ($server in $serverList) {
$tsGeneralSettings = Get-WmiObject "Win32_TSGeneralSetting" -ComputerName $server -Namespace root\cimv2\terminalservices -Filter "TerminalName='RDP-tcp'"
$sslCertificateHash = $tsGeneralSettings.SSLCertificateSHA1Hash

$output = New-Object -TypeName PSObject
$output | Add-Member -MemberType NoteProperty -Name "Server" -Value $server
$output | Add-Member -MemberType NoteProperty -Name "SSLCertificateSHA1Hash" -Value $sslCertificateHash

Write-Output $output | Format-Table
}

Массовая установка сертификата, который будет подставляться RDP службой к подключаемому серверу

Чуть выше мы проверяли сертификат, а вот если он не тот, что нужен на удаленном сервере, то нам необходимо его установить. Для этого нам нужно получить список удаленных серверов из файла, скопировать на них сертификат и установить его в локальное расположение компьютера и не забыть дать права на его чтение для учетной записи NETWORK SERVICE.

Из нюансов, ваш сертификат должен быть в формате PFX и располагаться совместно с вашим скриптом в одной директории.

#Создаем логи если их нет

function Date {Get-Date -Format "yyyy.MM.dd HH:mm:ss"}

$log_folder = "$PSScriptRoot\Logs\" + $($MyInvocation.MyCommand.Name -replace (".ps1", ""))
$log = "$log_folder\$(Get-Date -Format "yyyy_MM_dd_HH_mm_ss").txt"

if (! (Test-Path $log_folder -PathType Container -ErrorAction SilentlyContinue))
{
New-Item $log_folder -ItemType D -Force | Out-Null
}

"$(Date) Start Processing" | Tee-Object $log -Append

### Тут мы подгружаем файл со списком серверов

foreach ($server in (Get-Content "$PSScriptRoot\servers.txt"))
{
"$(Date) Trying to copy certificate file" | Tee-Object $log -Append

$Thumbprint = "3a9ba2991ca444779cdbac4211113c4adcaf1111"

Copy-Item -Path "$PSScriptRoot\pyatilistnik_new.pfx" -Destination "\\$server\C$\Windows\Temp\pyatilistnik_new.pfx" -Force -Verbose

### Установка удаленной сессии

"$(Date) Trying to create PS session and install certificate file" | Tee-Object $log -Append

$session = New-PSSession -ComputerName $server

Invoke-Command -Session $session -ScriptBlock {
$password = ConvertTo-SecureString -String "12345678" -AsPlainText -Force
Import-PFXCertificate -CertStoreLocation "Cert:\LocalMachine\My" -FilePath "C:\windows\temp\pyatilistnik_new.pfx" -Password $password -Verbose
}

Remove-PSSession $session

###

"$(Date) Trying to assign certificate to terminal services" | Tee-Object $log -Append

try {
$path = Get-WmiObject -Class "Win32_TSGeneralSetting" -Namespace "root\cimv2\terminalservices" -ComputerName $server -Verbose -ErrorAction Stop
Set-WmiInstance -Path $path -Arguments @{SSLCertificateSHA1Hash = $Thumbprint} -Verbose -ErrorAction Stop
}
catch {
"$(Date) $($_.exception.message)" | Tee-Object $log -Append
}
}

###

"$(Date) End Processing" | Tee-Object $log -Append

Данный скрипт решал проблему "Ошибка 0x907 при подключении к RDP" и ошибку 0x8009030D.

Массовая удаленная настройка файла конфигурации Zabbix

✅У меня в компании в качестве основного средства мониторинга используется Zabbix сервер, я все давно хочу написать о нем кучу полезных вещей по типу:

но все до этого не доходят руки, хотя материала огромное количество. Так как моя RDS ферма состоит из 50 хостов, и на каждом ручками нужно было настроить файл zabbix_agentd.conf, то я понял на сколько это нудно повторять одни и те же действия, а именно поменять в файле поля:

  • ListenIP=
  • Hostname=
  • SourceIP=

Чтобы это делать удаленно и по возможности на любое количество хостов, я написал два PowerShell скрипта.

Первый скрипт заполняет ListenIP и SourceIP беря текущий IP адрес сервера, Hostname= он берет из текстового файла по которому пробегает скрипт и сравнивает его с тем, что в системе, после чего проставляет его.

$filePath = "C:\Program Files\Zabbix Agent 2\zabbix_agent2.conf"
$currentIP = (Get-NetIPAddress | Where-Object {$_.AddressFamily -eq "IPv4" -and $_.InterfaceAlias -notlike "*Loopback*"}).IPAddress
$currentHostname = (Get-WmiObject Win32_ComputerSystem).Name

(Get-Content $filePath) |
ForEach-Object { $_ -replace 'ListenIP=.*', "ListenIP=$currentIP" } |
ForEach-Object { $_ -replace 'SourceIP=.*', "SourceIP=$currentIP" } |
ForEach-Object { $_ -replace 'Hostname=.*', "Hostname=$currentHostname" } |
Set-Content $filePath

Write-Host "Configuration file updated with current IP address and hostname."

Второй скрипт берет нужный список серверов из файла, после чего создает удаленную сессию и производит выполнение первого скрипта, который уже и подгоняет конфиг Zabbix под нужные требования.

serverList = Get-Content "C:\temp\servers.txt"

foreach ($server in $serverList) {
Invoke-Command -ComputerName $server -FilePath "C:\temp\Прописывание настроек Zabbix.ps1"
}

Тут главное поменять путь до правильного скрипта.

Удаленно перезапустить службу Zabbix на куче серверов

✅Это нужно после выполнения описанных выше скриптов, да и вообще в целом полезно это уметь, так как служба иногда может зависнуть или остановится. Тут два скрипта, в первом простая команда по перезапуску службы Zabbix 2, он называется Restart-Service Zabbix.ps1.

Restart-Service -Name "Zabbix Agent 2"

Второй скрипт запускает первый на списке удаленных хостов.

$serverList = Get-Content "C:\temp\servers.txt"

foreach ($server in $serverList) {
Invoke-Command -ComputerName $server -FilePath "C:\temp\Restart-Service Zabbix.ps1"
}

Как узнать статус службы удаленно на пуле серверов

После перезапуска службы Zabbix будет правильно узнать ее состояние. Для этого у вас должен быть список серверов в файле и мой скриптик.

# Получаем список серверов из файла и сохраняем его в переменную
$serverList = Get-Content "C:\Temp\RDS\servers.txt"

# Создаем пустой массив для хранения данных о статусе службы на каждом сервере
$statusData = @()

# Проходимся по каждому серверу в списке и проверяем статус службы Zabbix Agent 2
foreach ($server in $serverList) {
# Получаем текущий статус службы Zabbix Agent 2
$serviceStatus = Get-Service -ComputerName $server -Name "Zabbix Agent 2" | Select-Object -ExpandProperty Status

# Создаем объект для хранения данных о статусе на текущем сервере
$statusObj = [PSCustomObject]@{
ServerName = $server
ServiceStatus = $serviceStatus
}

# Добавляем объект в массив данных
$statusData += $statusObj
}

# Выводим данные в виде таблицы
$statusData | Format-Table -AutoSize ServerName, ServiceStatus

Удаленно узнаем состояние службы

Как получить текущие настройки Zabbix на пуле серверов

Еще из полезных скриптов могу отметить, что вы можете первым скриптом запросить текущие настройки ListenIP=, Hostname=, SourceIP=. А вторым скриптом выполнить первый удаленно и получить массовый результат.

#$filePath = "C:\Program Files\Zabbix Agent 2\zabbix_agent2.conf" #Тут зависит от версии вашего агента
$filePath = "C:\Program Files\Zabbix Agent\zabbix_agentd.conf"

$sourceIP = ""
$listenIP = ""
$hostname = ""

Get-Content $filePath | ForEach-Object {
if($_ -like "SourceIP=*") {
$sourceIP = $_.Split("=")[1]
}
elseif($_ -like "ListenIP=*") {
$listenIP = $_.Split("=")[1]
}
elseif($_ -like "Hostname=*") {
$hostname = $_.Split("=")[1]
}
}

$table = @()
$row = New-Object System.Object
$row | Add-Member -type NoteProperty -name "ComputerName" -value $env:COMPUTERNAME
$row | Add-Member -type NoteProperty -name "SourceIP" -value $sourceIP
$row | Add-Member -type NoteProperty -name "ListenIP" -value $listenIP
$row | Add-Member -type NoteProperty -name "Hostname" -value $hostname
$table += $row

$table | Format-Table ComputerName, SourceIP, ListenIP, Hostname

Powershell вывод из конфиг файла Zabbix

Тут правильно укажите путь и и имя до вашего скрипта, а так же информация сохранится в текстовый файл для анализа.

$serverList = Get-Content "C:\Temp\RDS\servers.txt"

foreach ($server in $serverList) {
Invoke-Command -ComputerName $server -FilePath "C:\Temp\RDS\Получить значения конфига Zabbix локально.ps1" | Out-File -Append "C:\temp\Zabbix_conf.txt"
}

$zabbixConf = Get-Content "C:\temp\Zabbix_conf.txt" | Select-String "ComputerName|SourceIP|ListenIP|Hostname"

$zabbixConf | Out-File "C:\temp\Zabbix_conf_2.txt"

Массовое копирование папки или ярлыка на список серверов

Когда вы кастомизируете ваша RDSH хосты или появилась необходимость на все узлы добавить новую папку с приложением (Например что-то самописное) и вывести ярлык на общий рабочий стол, то можете за основу взять такой скрипт.

$serverList = Get-Content -Path "C:\Temp\servers.txt" #Список серверов куда копируем
$sourceFolder = "C:\Temp\Soft\WordMerge" #Папку, которую будем копировать
$destinationFolder = "c$\Program Files (x86)\pyatilistnik Apps" #Куда будем копировать
$fileToCopy = "C:\Temp\Soft\McdSoft.Crm.WordMergeUtil.lnk" #Копируемый файл ярлык

foreach ($server in $serverList) {
if (Test-Path "\\$server\$destinationFolder" -PathType Container) {
Write-Host "Copying $sourceFolder to \\$server\$destinationFolder..."
if (Test-Path $sourceFolder -PathType Container) {
Copy-Item -Path $sourceFolder -Destination "\\$server\$destinationFolder" -Recurse
Write-Host "Copy complete."
} else {
Write-Warning "$sourceFolder not found."
}
} else {
Write-Warning "\\$server\$destinationFolder not found."
}
}

foreach ($server in $serverList) {
Copy-Item -Path $fileToCopy -Destination "\\$server\C$\Users\Public\Desktop"
}

Проверка ошибок RDS на брокере

Когда я искал причину ошибки "Remote Desktop Connection Broker Client failed to redirect the user" я долго изучал журналы ошибок Windows на брокерах, чтобы ускорить этот процесс вот небольшой скрптик, который проверяет все ошибки, что есть в журнале Microsoft-Windows-TerminalServices- SessionBroker-Client/Operational.

Get-WinEvent -LogName Microsoft-Windows-TerminalServices-SessionBroker-Client/Operational -ErrorAction SilentlyContinue | Where-Object {$_.LevelDisplayName -eq "Error"} | FL TimeCreated, Message

Как получить статус DFSS на всех серверах RDSH

Ранее я вам рассказывал в двух статьях, что из-за некого механизма Dynamic Fair Share Scheduling (DFSS) (Балансировка нагрузки) у меня медленно работало приложение 1С и жутко тормозило приложение Directum. Там мы выключали механизм балансировки. Для удобства проверки я создал сриптик, который за пару секунд делает выгрузку по текущему состоянию EnabledFss.

$Servers = Get-Content "C:\temp\servers.txt"
foreach ($Server in $Servers) {
$EnabledFss = gwmi win32_terminalservicesetting -ComputerName $Server -Namespace "root\cimv2\terminalservices" | Select-Object enabledfss
$Table = New-Object -TypeName PSObject

$Table | Add-Member -MemberType NoteProperty -Name "Server" -Value $Server
$Table | Add-Member -MemberType NoteProperty -Name "EnabledFss" -Value $EnabledFss.enabledfss
$Table | Format-Table -AutoSize
}

Проверка Dynamic Fair Share Scheduling (DFSS)

Массово получить информацию о CPU, RAM, SSD

Иногда нужно быстро проверить все ли характеристики у терминальных серверов одинаковые, сравнив CPU, RAM, SSD. Я написал простой скрипт, который это делает за пару минут.

# Получаем список серверов из файла
$servers = Get-Content -Path "C:\Temp\RDS\servers.txt"

# Создаем пустой массив для хранения результатов
$result = @()

# Для каждого сервера в списке
foreach ($server in $servers) {

# Получаем информацию о системе
$system = Get-WmiObject -Class Win32_ComputerSystem -ComputerName $server

# Получаем информацию о процессоре
$processor = Get-WmiObject -Class Win32_Processor -ComputerName $server

# Получаем информацию о дисках
$disks = Get-WmiObject -Class Win32_LogicalDisk -Filter "DriveType=3" -ComputerName $server

# Создаем объект с результатами
$object = New-Object -TypeName PSObject -Property @{
ServerName = $server
Memory = [Math]::Round($system.TotalPhysicalMemory / 1MB, 2)
CPU = $processor.Name
DiskSize = [Math]::Round(($disks | Measure-Object -Property Size -Sum).Sum / 1GB, 2)
}

# Добавляем объект в массив результатов
$result += $object
}

# Выводим результаты в виде таблицы
$result | Format-Table -AutoSize

Массово получить информацию о CPU, RAM, SSD

P.S.

По мере написания еще каких-то скриптов, я буду обновлять текущую статью. Если у вас есть, что-то полезное, то пишите об этом в комментариях.

Автор - Сёмин Иван

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *