Полезные скрипты для RDS фермы
- Как использовать данные скрипты
- Скрипт по переводу RDSH в режим стока
- Скрипт по поиску событий RDS для определенного логина
- Массовая проверка отпечатка сертификата используемого при RDP сессии на удаленных серверах
- Массовая установка сертификата, который будет подставляться RDP службой к подключаемому серверу
- Массовая удаленная настройка файла конфигурации Zabbix
- Удаленно перезапустить службу Zabbix на куче серверов
- Как узнать статус службы удаленно на пуле серверов
- Как получить текущие настройки Zabbix на пуле серверов
- Массовое копирование папки или ярлыка на список серверов
- Проверка ошибок RDS на брокере
- Как получить статус DFSS на всех серверах RDSH
- Массово получить информацию о CPU, RAM, SSD
- P.S.
Добрый день! Уважаемые читатели и гости IT блога Pyatilistnik.org. В минувший раз мы с вами разобрали, что такое современные центры обработки данных, как они работаю и как без них уже невозможно представить жизнь в цифровом мире. Сегодня я хочу в данной статье поделиться огромной и полезной подборкой скриптов PowerShell по обслуживанию RDS фермы. И мне так проще будет их искать и не затерять, да и вам что-то, да пригодится в своей работе, я всегда за то, чтобы помогать друг другу прокачивать знания, не люблю я жадин таких, кто не делится опытом. Так же жду от вас полезные скрипты и что вы ими решаете в комментариях, самые полезные добавлю в саму статью.
Как использовать данные скрипты
- ✅Во первых используйте для запуска скриптов PowerShell ISE в режиме администратора
- ✅Запускать скрипты нужно с правами учетной записи у которой есть права на конечные серверы, чаще всего это администратор домена или леса, но у меня для этого отдельная учетная запись с правами администратора на всю RDS ферму.
- ✅Лично я для удобства выполнения скриптов выполняю их на самом брокере,
Скрипт по переводу 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
}
}
}
}
Массовая проверка отпечатка сертификата используемого при 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.
Второй скрипт запускает первый на списке удаленных хостов.
$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
Тут правильно укажите путь и и имя до вашего скрипта, а так же информация сохранится в текстовый файл для анализа.
$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.
Как получить статус DFSS на всех серверах RDSH
Ранее я вам рассказывал в двух статьях, что из-за некого механизма Dynamic Fair Share Scheduling (DFSS) (Балансировка нагрузки) у меня медленно работало приложение 1С и жутко тормозило приложение Directum. Там мы выключали механизм балансировки. Для удобства проверки я создал сриптик, который за пару секунд делает выгрузку по текущему состоянию EnabledFss.
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
}
Массово получить информацию о 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
P.S.
По мере написания еще каких-то скриптов, я буду обновлять текущую статью. Если у вас есть, что-то полезное, то пишите об этом в комментариях.