Загрузка данных
Import-Module ActiveDirectory
$GatewayNodes = @(
'sgo-ts045-1.go.rshbank.ru'
# остальные ноды
)
$Days = 30
$StartDate = (Get-Date).AddDays(-$Days)
Write-Host "Запуск сбора с нод RDGW..." -ForegroundColor Cyan
$RemoteScript = {
param($Since)
$Filter = @{
LogName = 'Microsoft-Windows-TerminalServices-Gateway/Operational'
Id = @(300, 301)
StartTime = $Since
}
try {
$events = Get-WinEvent -FilterHashtable $Filter -ErrorAction Stop
foreach ($e in $events) {
$xml = [xml]$e.ToXml()
$info = $xml.Event.UserData.EventInfo
$user = if ($info.Username) { $info.Username } else { $info.UsrName }
$target = if ($info.Resource) { $info.Resource } else { $info.ResourceName }
$ip = if ($info.IpAddress) { $info.IpAddress } else { $info.ClientIp }
if ($user -and $target) {
[PSCustomObject]@{
GatewayHost = $env:COMPUTERNAME
Timestamp = $e.TimeCreated
UserName = $user
TargetHost = $target
ClientIP = $ip
}
}
}
}
catch {
Write-Warning "Ошибка на $($env:COMPUTERNAME): $_"
}
}
$AllConnections = Invoke-Command -ComputerName $GatewayNodes -ScriptBlock $RemoteScript -ArgumentList $StartDate -ThrottleLimit 4
if (-not $AllConnections) {
Write-Warning "События не получены."
return
}
Write-Host "Собрано сырых записей: $($AllConnections.Count). Начинаем обработку и проверку в AD/DNS..." -ForegroundColor Cyan
# Кэши для снижения нагрузки на AD и DNS
$UserStatusCache = @{}
$DnsCache = @{}
# Вспомогательная функция проверки пользователя в AD
function Test-UserIsActive ($rawUserName) {
# Отрезаем домен (DOMAIN\user -> user)
$samAccount = ($rawUserName -split '\\')[-1].Trim().ToLower()
if ($UserStatusCache.ContainsKey($samAccount)) {
return $UserStatusCache[$samAccount]
}
try {
$adUser = Get-ADUser -Identity $samAccount -ErrorAction Stop
# Проверяем, что найден и включен (Enabled: True)
$isActive = ($adUser -ne $null) -and ($adUser.Enabled -eq $true)
$UserStatusCache[$samAccount] = $isActive
return $isActive
}
catch {
# Пользователь не найден или удалён
$UserStatusCache[$samAccount] = $false
return $false
}
}
# Вспомогательная функция резолва IP (10.x.x.x -> FQDN)
function Resolve-HostTarget ($target) {
$target = $target.Trim().ToLower()
# Проверяем, начинается ли с 10. (IP-адрес)
if ($target -match '^10\.\d{1,3}\.\d{1,3}\.\d{1,3}$') {
if ($DnsCache.ContainsKey($target)) {
return $DnsCache[$target]
}
try {
# Быстрый reverse DNS (PTR)
$resolved = [System.Net.Dns]::GetHostEntry($target).HostName.ToLower()
$DnsCache[$target] = $resolved
return $resolved
}
catch {
# Если в PTR зоне нет записи, оставляем исходный IP
$DnsCache[$target] = $target
return $target
}
}
return $target
}
# 1. Фильтруем и преобразуем хосты
$ProcessedConnections = foreach ($item in $AllConnections) {
if (-not (Test-UserIsActive $item.UserName)) {
continue
}
[PSCustomObject]@{
GatewayHost = $item.GatewayHost
Timestamp = $item.Timestamp
UserName = ($item.UserName -split '\\')[-1].ToLower()
TargetHost = Resolve-HostTarget $item.TargetHost
ClientIP = $item.ClientIP
}
}
# 2. Группируем по уникальным связкам уже с обновленными именами ПК
$Mapping = $ProcessedConnections |
Group-Object -Property UserName, TargetHost |
ForEach-Object {
$Latest = $_.Group | Sort-Object Timestamp -Descending | Select-Object -First 1
[PSCustomObject]@{
UserName = $Latest.UserName
TargetHost = $Latest.TargetHost
LastConnection = $Latest.Timestamp
LastGateway = $Latest.GatewayHost
LastClientIP = $Latest.ClientIP
TotalHits = $_.Count
}
} |
Sort-Object UserName, TargetHost
# 3. Экспорт в CSV без кавычек
$OutPath = "C:\Temp\rdgw_cluster_mapping_clean.csv"
$Mapping | ConvertTo-Csv -NoTypeInformation | ForEach-Object { $_ -replace '"', '' } | Set-Content -Path $OutPath -Encoding UTF8
Write-Host "Готово! Сохранено активных связок: $($Mapping.Count) в $OutPath" -ForegroundColor Green