Перейти к содержимому

Как найти битые профили fslogix и найти профили которые не монтировались на сервер

Проблема что есть хосты на которых не монтируются профили надо найти какие профили не монтируются и почему

# === Настройки ===
$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:

Метки:

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

Яндекс.Метрика