Проблема что есть хосты на которых не монтируются профили надо найти какие профили не монтируются и почему
# === Настройки ===
$collectionName = "Farm Fxlogixs"
$connectionBroker = "RDCB02.adminbd.ru"
$fslogixRootUnc = "\\HWRDU\DisksfsFSLogix$"
$MaxDisksToCheck = 1000 # Проверяем только первые N проблемных дисков
# === Логирование ===
$LogDir = "$env:TEMP\FSLogixVHDXCheck"
if (-not (Test-Path $LogDir)) { New-Item -Path $LogDir -ItemType Directory -Force | Out-Null }
$LogPath = "$LogDir\FSLogix_VHDX_Check_$(Get-Date -Format 'yyyy-MM-dd_HH-mm-ss').log"
function Write-Log {
param([string]$Message, [string]$Level = "INFO")
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$logEntry = "[$timestamp] [$Level] $Message"
Write-Host $logEntry
Add-Content -Path $LogPath -Value $logEntry
}
Write-Log "=== НАЧАЛО ПРОВЕРКИ VHDX ДЛЯ ПОЛЬЗОВАТЕЛЕЙ БЕЗ ПРОФИЛЯ ===" "INFO"
Write-Log "Коллекция: $collectionName | Брокер: $connectionBroker" "INFO"
Write-Log "Путь к дискам: $fslogixRootUnc" "INFO"
Write-Log "Лимит проверки: первые $MaxDisksToCheck дисков" "INFO"
# === 1. Получаем все сессии из коллекции ===
Write-Log "Получение сессий из коллекции '$collectionName' через брокер '$connectionBroker'..." "INFO"
try {
$sessions = Get-RDUserSession -CollectionName $collectionName -ConnectionBroker $connectionBroker -ErrorAction Stop
} catch {
Write-Log "❌ Ошибка при получении сессий: $($_.Exception.Message)" "ERROR"
exit 1
}
if (-not $sessions) {
Write-Log "ℹ️ Нет сессий в коллекции." "INFO"
exit 0
}
# === 2. Фильтруем: только сессии с заполненным HostServer ===
$validSessions = $sessions | Where-Object {
$_.HostServer -and $_.HostServer.Trim() -ne ''
}
Write-Log "Всего сессий: $($sessions.Count). Валидных (с HostServer): $($validSessions.Count)" "INFO"
if (-not $validSessions) {
Write-Log "❌ Нет валидных сессий для проверки." "WARN"
exit 0
}
# === 3. Находим пользователей без профиля ===
$sessionsByHost = $validSessions | Group-Object HostServer
$problemUsers = @()
Write-Log "Поиск пользователей с активной сессией, но без смонтированного профиля..." "INFO"
foreach ($group in $sessionsByHost) {
$hostName = $group.Name.Trim()
$hostSessions = $group.Group
try {
$userNamesToCheck = $hostSessions.UserName | ForEach-Object { ($_ -split '\\')[-1] }
$mountedUsers = Invoke-Command -ComputerName $hostName -ScriptBlock {
param($expectedNames)
$mounted = @()
$volumes = Get-Volume -ErrorAction SilentlyContinue |
Where-Object { $_.FileSystemLabel -and $_.FileSystemLabel.StartsWith("Profile-") }
foreach ($vol in $volumes) {
$userNameFromLabel = $vol.FileSystemLabel.Substring("Profile-".Length)
$userNameClean = ($userNameFromLabel -split '\\')[-1].Trim()
foreach ($exp in $expectedNames) {
if ($userNameClean.ToLower() -eq $exp.ToLower()) {
$mounted += $exp
break
}
}
}
return $mounted
} -ArgumentList (,$userNamesToCheck) -ErrorAction Stop
foreach ($session in $hostSessions) {
$userName = ($session.UserName -split '\\')[-1]
if ($userName -notin $mountedUsers) {
$problemUsers += [PSCustomObject]@{
FullUserName = $session.UserName
UserName = $userName
SessionHost = $session.HostServer
SessionId = $session.UnifiedSessionId
SessionState = $session.SessionState
}
Write-Log "⚠️ Обнаружена проблема: пользователь '$userName' на хосте '$hostName' — профиль не смонтирован." "WARN"
}
}
} catch {
$errMsg = "Не удалось проверить хост '$hostName': $($_.Exception.Message)"
Write-Log $errMsg "ERROR"
foreach ($session in $hostSessions) {
$userName = ($session.UserName -split '\\')[-1]
$problemUsers += [PSCustomObject]@{
FullUserName = $session.UserName
UserName = $userName
SessionHost = $session.HostServer
SessionId = $session.UnifiedSessionId
SessionState = $session.SessionState
}
Write-Log "⚠️ Пользователь '$userName' помечен как проблемный из-за ошибки проверки хоста." "WARN"
}
}
}
if (-not $problemUsers) {
Write-Log "✅ Все пользователи с сессией имеют смонтированный профиль. Проверка не требуется." "INFO"
exit 0
}
# === Ограничиваем до первых N ===
if ($problemUsers.Count -gt $MaxDisksToCheck) {
Write-Log "Найдено $($problemUsers.Count) проблемных пользователей. Проверим только первые $MaxDisksToCheck." "INFO"
$problemUsers = $problemUsers | Select-Object -First $MaxDisksToCheck
} else {
Write-Log "Будет проверено $($problemUsers.Count) VHDX-файлов." "INFO"
}
# === 4. Проверка VHDX-файлов напрямую через Get-Volume ===
$vhdxCheckResults = @()
foreach ($user in $problemUsers) {
Write-Log "`n--- Проверка пользователя: $($user.UserName) ---" "INFO"
$foundVhdx = $false
$vhdxPath = ""
# Поиск папки и VHDX-файла
try {
Write-Log "Поиск папок по маске: '$($user.UserName)*' в $fslogixRootUnc" "DEBUG"
$userFolders = Get-ChildItem -Path $fslogixRootUnc -Directory -ErrorAction Stop |
Where-Object { $_.Name -like "$($user.UserName)*" }
if ($userFolders) {
foreach ($folder in $userFolders) {
$candidate1 = Join-Path $folder.FullName "Profile_$($user.UserName).VHDX"
$candidate2 = Join-Path $folder.FullName "Profile_$($user.UserName).vhdx"
if (Test-Path $candidate1) {
$vhdxPath = $candidate1
$foundVhdx = $true
Write-Log "Найден VHDX: $vhdxPath" "INFO"
break
}
if (Test-Path $candidate2) {
$vhdxPath = $candidate2
$foundVhdx = $true
Write-Log "Найден VHDX: $vhdxPath" "INFO"
break
}
}
} else {
Write-Log "Папки для пользователя '$($user.UserName)' не найдены." "WARN"
}
} catch {
Write-Log "Ошибка при поиске папок: $($_.Exception.Message)" "ERROR"
}
# === Проверка через Get-Volume (без Get-Disk!) ===
$fsType = "N/A"
$errorDetail = ""
$status = "OK"
if (-not $foundVhdx) {
$status = "MISSING"
$errorDetail = "VHDX-файл не найден"
Write-Log "❌ VHDX не найден для пользователя '$($user.UserName)'" "ERROR"
} else {
try {
Write-Log "Монтирование VHDX напрямую с UNC: $vhdxPath" "DEBUG"
$diskImage = Mount-DiskImage -ImagePath $vhdxPath -NoDriveLetter -ErrorAction Stop -PassThru
Start-Sleep -Seconds 2
# Ищем том по метке
$expectedLabel = "Profile-$($user.UserName)"
$volume = Get-Volume -ErrorAction SilentlyContinue |
Where-Object { $_.FileSystemLabel -and $_.FileSystemLabel.Trim() -eq $expectedLabel }
if ($volume) {
$fsType = $volume.FileSystem
if ($fsType -eq "RAW") {
$status = "CORRUPT"
$errorDetail = "Том в состоянии RAW"
Write-Log "❌ Найден том '$expectedLabel', файловая система: RAW" "ERROR"
} elseif ($volume.HealthStatus -ne "Healthy") {
$status = "CORRUPT"
$errorDetail = "Том в состоянии: $($volume.HealthStatus)"
Write-Log "❌ Том '$expectedLabel' не здоров: $($volume.HealthStatus)" "ERROR"
} else {
Write-Log "✅ Том '$expectedLabel' в порядке: $fsType, состояние: $($volume.HealthStatus)" "INFO"
}
} else {
# Том не найден по метке — проверим, есть ли вообще тома
$allVolumes = Get-Volume -ErrorAction SilentlyContinue | Where-Object { $_.Size -gt 100MB }
if ($allVolumes) {
$status = "CORRUPT"
$errorDetail = "Тома есть, но метка '$expectedLabel' не найдена"
Write-Log "⚠️ Тома обнаружены, но без ожидаемой метки '$expectedLabel'" "WARN"
Write-Log " Доступные метки: $(($allVolumes.FileSystemLabel -join ', '))" "DEBUG"
} else {
$status = "CORRUPT"
$errorDetail = "После монтирования тома не обнаружены"
Write-Log "❌ Тома не найдены после монтирования" "ERROR"
}
}
} catch {
$status = "CORRUPT"
$errorDetail = "Ошибка монтирования: $($_.Exception.Message)"
Write-Log "❌ $errorDetail" "ERROR"
} finally {
# Отмонтирование
try {
Write-Log "Отмонтирование VHDX: $vhdxPath" "DEBUG"
Dismount-DiskImage -ImagePath $vhdxPath -ErrorAction SilentlyContinue | Out-Null
Write-Log "✅ VHDX отмонтирован" "DEBUG"
} catch {
Write-Log "⚠️ Ошибка при отмонтировании: $($_.Exception.Message)" "WARN"
}
}
}
$result = [PSCustomObject]@{
UserName = $user.UserName
VhdxPath = if ($foundVhdx) { $vhdxPath } else { "Не найден" }
Status = $status
FileSystem = $fsType
Error = $errorDetail
}
$vhdxCheckResults += $result
}
# === 5. Итоговый отчёт ===
Write-Log "`n=== ИТОГОВЫЙ ОТЧЁТ ===" "INFO"
$bad = $vhdxCheckResults | Where-Object { $_.Status -ne "OK" }
if ($bad) {
Write-Log "❌ Найдены проблемы у $($bad.Count) из $($vhdxCheckResults.Count) проверенных дисков:" "ERROR"
$bad | ForEach-Object {
Write-Log " - $($_.UserName): $($_.Error)" "ERROR"
}
} else {
Write-Log "✅ Все проверенные VHDX-файлы в порядке." "INFO"
}
# Сохраняем CSV
$csvPath = "$LogDir\VHDX_Report_$(Get-Date -Format 'yyyy-MM-dd_HH-mm-ss').csv"
$vhdxCheckResults | Export-Csv -Path $csvPath -NoTypeInformation -Encoding UTF8
Write-Log "📄 Отчёты сохранены:`n Лог: $LogPath`n CSV: $csvPath" "INFO"
Write-Host "`n✅ Проверка завершена. Подробный лог: $LogPath" -ForegroundColor Cyan
После проверки удалить битые профили
# === Настройки ===
#$csvPath = "$env:TEMP\FSLogixVHDXCheck\VHDX_Report_2025-04-05_10-30-00.csv" # ← Укажите актуальный путь или получите динамически
# Если вы хотите автоматически взять последний CSV из папки логов:
$LogDir = "$env:TEMP\FSLogixVHDXCheck"
$csvPath = Get-ChildItem -Path $LogDir -Filter "VHDX_Report_*.csv" | Sort-Object LastWriteTime -Descending | Select-Object -First 1 -ExpandProperty FullName
if (-not (Test-Path $csvPath)) {
Write-Error "❌ Файл CSV не найден: $csvPath"
exit 1
}
# === Логирование ===
$LogDir = "$env:TEMP\FSLogixVHDXCheck"
if (-not (Test-Path $LogDir)) { New-Item -Path $LogDir -ItemType Directory -Force | Out-Null }
$LogPath = "$LogDir\FSLogix_DeleteFolders_$(Get-Date -Format 'yyyy-MM-dd_HH-mm-ss').log"
function Write-Log {
param([string]$Message, [string]$Level = "INFO")
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$logEntry = "[$timestamp] [$Level] $Message"
Write-Host $logEntry
Add-Content -Path $LogPath -Value $logEntry
}
Write-Log "=== НАЧАЛО УДАЛЕНИЯ ПАПОК FSLogix на основе CSV ===" "INFO"
Write-Log "CSV-файл: $csvPath" "INFO"
# === Чтение CSV ===
try {
$csvData = Import-Csv -Path $csvPath -Encoding UTF8
Write-Log "Загружено записей из CSV: $($csvData.Count)" "INFO"
} catch {
Write-Log "❌ Ошибка при чтении CSV: $($_.Exception.Message)" "ERROR"
exit 1
}
# === Фильтрация: только где FileSystem = 'N/A' (VHDX не найден) ===
$toDelete = $csvData | Where-Object { $_.FileSystem -eq "N/A" -and $_.VhdxPath -ne "Не найден" }
if (-not $toDelete) {
Write-Log "ℹ️ Нет записей с FileSystem = 'N/A' для удаления." "INFO"
exit 0
}
Write-Log "Найдено записей для удаления: $($toDelete.Count)" "INFO"
$deletedCount = 0
$errorCount = 0
foreach ($record in $toDelete) {
$vhdxPath = $record.VhdxPath
# Извлекаем папку (родительский каталог VHDX)
try {
$folderPath = Split-Path -Path $vhdxPath -Parent
} catch {
Write-Log "⚠️ Невозможно определить папку из пути: '$vhdxPath'" "WARN"
$errorCount++
continue
}
# Проверка: путь должен быть внутри FSLogix-шары и выглядеть как профиль
if ($folderPath -notlike "\\*\*\*" -or $folderPath -notmatch "\\[^\\]+\\[^\\]+$") {
Write-Log "⚠️ Пропуск подозрительного пути (не похож на папку профиля): '$folderPath'" "WARN"
$errorCount++
continue
}
Write-Log "Попытка удаления папки: $folderPath" "INFO"
if (Test-Path $folderPath) {
try {
Remove-Item -Path $folderPath -Recurse -Force -ErrorAction Stop
Write-Log "✅ Успешно удалена папка: $folderPath" "INFO"
$deletedCount++
} catch {
Write-Log "❌ Ошибка при удалении '$folderPath': $($_.Exception.Message)" "ERROR"
$errorCount++
}
} else {
Write-Log "ℹ️ Папка уже отсутствует: $folderPath" "INFO"
# Считаем как "успешно", т.к. цель — отсутствие папки
$deletedCount++
}
}
# === Итог ===
Write-Log "`n=== ИТОГ УДАЛЕНИЯ ===" "INFO"
Write-Log "Успешно (удалено или уже отсутствовало): $deletedCount" "INFO"
Write-Log "Ошибок: $errorCount" "INFO"
Write-Log "📄 Лог операций: $LogPath" "INFO"
Write-Host "`n✅ Операция завершена. Лог: $LogPath" -ForegroundColor Cyan
Наглядно у кого не смонтирован
# === Настройки ===
$collectionName = "Farm Fxlogixs"
$connectionBroker = "MSK-HWRDCB02.voxys.ru"
# === 1. Получаем все сессии из коллекции ===
Write-Host "Получение сессий из коллекции '$collectionName' через брокер '$connectionBroker'..." -ForegroundColor Cyan
try {
$sessions = Get-RDUserSession -CollectionName $collectionName -ConnectionBroker $connectionBroker -ErrorAction Stop
} catch {
Write-Error "❌ Ошибка при получении сессий: $($_.Exception.Message)"
exit 1
}
if (-not $sessions) {
Write-Host "ℹ️ Нет сессий в коллекции." -ForegroundColor Green
exit 0
}
# === 2. Фильтруем: только сессии с заполненным HostServer ===
$validSessions = $sessions | Where-Object {
$_.HostServer -and $_.HostServer.Trim() -ne ''
}
Write-Host "Всего сессий: $($sessions.Count)" -ForegroundColor Gray
Write-Host "Валидных (с HostServer): $($validSessions.Count)" -ForegroundColor Cyan
if (-not $validSessions) {
Write-Host "❌ Нет валидных сессий для проверки." -ForegroundColor Red
exit 0
}
# === 3. Группируем сессии по хосту ===
$sessionsByHost = $validSessions | Group-Object HostServer
$problemUsers = @()
# === 4. Проверяем каждый хост ===
foreach ($group in $sessionsByHost) {
$hostName = $group.Name.Trim()
$hostSessions = $group.Group
Write-Host "`n🔍 Проверка хоста: $hostName" -ForegroundColor Yellow
try {
# Имена пользователей без домена
$userNamesToCheck = $hostSessions.UserName | ForEach-Object { ($_ -split '\\')[-1] }
# === Проверка через FileSystemLabel (рабочий способ) ===
$mountedUsers = Invoke-Command -ComputerName $hostName -ScriptBlock {
param($expectedNames)
$mounted = @()
# Используем FileSystemLabel — именно там FSLogix пишет имя
$volumes = Get-Volume -ErrorAction SilentlyContinue |
Where-Object { $_.FileSystemLabel -and $_.FileSystemLabel.StartsWith("Profile-") }
foreach ($vol in $volumes) {
# Извлекаем имя: "Profile-Olga.Y.Mishchenko" → "Olga.Y.Mishchenko"
$userNameFromLabel = $vol.FileSystemLabel.Substring("Profile-".Length)
# Удаляем домен (если вдруг есть)
$userNameClean = ($userNameFromLabel -split '\\')[-1].Trim()
# Сравниваем без учёта регистра
foreach ($exp in $expectedNames) {
if ($userNameClean -eq $exp -or $userNameClean -eq $exp.ToLower() -or $userNameClean.ToLower() -eq $exp.ToLower()) {
$mounted += $exp
break
}
}
}
return $mounted
} -ArgumentList (,$userNamesToCheck) -ErrorAction Stop
# === Сравниваем: кто не смонтирован? ===
foreach ($session in $hostSessions) {
$userName = ($session.UserName -split '\\')[-1]
if ($userName -notin $mountedUsers) {
$problemUsers += [PSCustomObject]@{
UserName = $session.UserName
SessionHost = $session.HostServer
SessionId = $session.UnifiedSessionId
SessionState = $session.SessionState
Status = "Профиль НЕ смонтирован (том отсутствует)"
}
Write-Host "⚠️ $userName — профиль не смонтирован!" -ForegroundColor Red
} else {
Write-Host "✅ $userName — профиль смонтирован." -ForegroundColor Green
}
}
} catch {
Write-Warning "🚫 Не удалось проверить хост '$hostName': $($_.Exception.Message)"
foreach ($session in $hostSessions) {
$problemUsers += [PSCustomObject]@{
UserName = $session.UserName
SessionHost = $session.HostServer
SessionId = $session.UnifiedSessionId
SessionState = $session.SessionState
Status = "Ошибка проверки (недоступен хост / WinRM / нет прав)"
}
}
}
}
# === 5. Итоговый отчёт ===
if ($problemUsers) {
Write-Host "`n" + ("=" * 90) -ForegroundColor Gray
Write-Host "❌ НАЙДЕНЫ ПРОБЛЕМЫ: активная сессия есть, но FSLogix-том не смонтирован" -ForegroundColor Red
Write-Host ("=" * 90) -ForegroundColor Gray
$problemUsers | Format-Table -AutoSize
} else {
Write-Host "`n✅ У всех пользователей с сессией FSLogix-профиль смонтирован корректно." -ForegroundColor Green
}
Similar Posts:
- Как вывести список пользователей у которых не при монтировался диск fslogix и переименовать FriendlyName
- Как сбросить все сессии в статусе дисконект (disconnect) с фермы rds windows
- Как завершить отключение сессии на серверах RDS и вывести список где пользователи не найдены на хостах но в брокере они есть.
- Как завершить сессию на всех серверах в локальной сети.
- Как разлогинеть (logoff) всех пользователей со статусом disconnect со всех серверов windows в domain.