Практика / рабочие места

Как применять материал

Материал стоит воспринимать как короткий runbook: сначала сохранить рабочее состояние, затем проверить симптом, выполнить минимально достаточное исправление и зафиксировать результат. Так единичная проблема не превращается в бесконечную переписку.

01 Симптом
02 Диагностика
03 Исправление
04 Контроль

Практический порядок

  1. открыть PowerShell от имени администратора и сохранить рабочие документы
  2. запустить скрипт в стандартном режиме: он создаст лог, проверит DISM/SFC и очистит только старые временные файлы
  3. если есть явные проблемы Windows Update, повторить запуск с ResetWindowsUpdateCache=true
  4. после перезагрузки проверить журнал, Windows Update и исходный симптом

Улучшенный PowerShell-скрипт

Совместимость: Windows 10/11, Windows Server 2016-2025; PowerShell 5.1 или PowerShell 7; запуск от администратора.

Скопируйте код в файл, например `WDI-WindowsRepair.ps1`, запустите PowerShell от имени администратора и выполните скрипт. Сброс кэша Windows Update выключен по умолчанию; временные файлы удаляются только если они старше заданного возраста. Для более жесткого сценария используйте параметр `-ResetWindowsUpdateCache $true`.

<#
WDI Windows Repair / Health Check
Запускать в PowerShell от имени администратора.

Что делает:
- проверяет состояние образа Windows через DISM;
- запускает восстановление DISM RestoreHealth;
- проверяет системные файлы через SFC;
- пишет подробный лог;
- очищает только старые временные файлы;
- опционально сбрасывает кэш Windows Update.
#>

[CmdletBinding()]
param(
    [bool]$CreateRestorePoint = $true,
    [bool]$CleanTemp = $true,
    [int]$TempOlderThanDays = 2,
    [bool]$ResetWindowsUpdateCache = $false,
    [string]$LogPath = "$env:SystemDrive\WDI-WindowsRepair"
)

$ErrorActionPreference = "Continue"

$principal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())
if (-not $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
    throw "Запустите PowerShell от имени администратора."
}

New-Item -ItemType Directory -Path $LogPath -Force | Out-Null
$stamp = Get-Date -Format "yyyyMMdd-HHmmss"
$transcriptPath = Join-Path $LogPath "windows-repair-$stamp.log"

function Write-Step {
    param([Parameter(Mandatory)][string]$Message)
    Write-Host ""
    Write-Host "== $Message ==" -ForegroundColor Cyan
}

function Invoke-LoggedProcess {
    param(
        [Parameter(Mandatory)][string]$FilePath,
        [Parameter(Mandatory)][string[]]$Arguments,
        [Parameter(Mandatory)][string]$Name
    )

    Write-Step $Name
    $process = Start-Process -FilePath $FilePath -ArgumentList $Arguments -Wait -PassThru -NoNewWindow
    if ($process.ExitCode -ne 0) {
        Write-Warning "$Name завершился с кодом $($process.ExitCode). Проверьте лог."
    }
}

Start-Transcript -Path $transcriptPath -Force | Out-Null

try {
    if ($CreateRestorePoint) {
        Write-Step "Создание точки восстановления"
        try {
            Checkpoint-Computer -Description "Before WDI Windows repair $stamp" -RestorePointType "MODIFY_SETTINGS"
        } catch {
            Write-Warning "Точка восстановления не создана: $($_.Exception.Message)"
        }
    }

    Write-Step "Информация о системе"
    if (Get-Command Get-ComputerInfo -ErrorAction SilentlyContinue) {
        Get-ComputerInfo | Select-Object WindowsProductName, WindowsVersion, OsBuildNumber, OsArchitecture | Format-List
    } else {
        Get-CimInstance Win32_OperatingSystem | Select-Object Caption, Version, BuildNumber, OSArchitecture | Format-List
    }

    Invoke-LoggedProcess -FilePath "dism.exe" -Arguments @("/Online", "/Cleanup-Image", "/CheckHealth") -Name "DISM CheckHealth"
    Invoke-LoggedProcess -FilePath "dism.exe" -Arguments @("/Online", "/Cleanup-Image", "/ScanHealth") -Name "DISM ScanHealth"
    Invoke-LoggedProcess -FilePath "dism.exe" -Arguments @("/Online", "/Cleanup-Image", "/RestoreHealth") -Name "DISM RestoreHealth"
    Invoke-LoggedProcess -FilePath "sfc.exe" -Arguments @("/scannow") -Name "SFC /scannow"

    if ($ResetWindowsUpdateCache) {
        Write-Step "Сброс кэша Windows Update"
        $updateServices = @("wuauserv", "bits", "cryptsvc")
        try {
            foreach ($service in $updateServices) {
                Stop-Service -Name $service -Force -ErrorAction Stop
            }

            foreach ($folder in @(
                (Join-Path $env:SystemRoot "SoftwareDistribution"),
                (Join-Path $env:SystemRoot "System32\catroot2")
            )) {
                if (Test-Path -LiteralPath $folder) {
                    $backupName = "$(Split-Path $folder -Leaf).old-$stamp"
                    Rename-Item -LiteralPath $folder -NewName $backupName -ErrorAction Stop
                    Write-Host "Папка переименована: $folder -> $backupName"
                }
            }
        } catch {
            Write-Warning "Кэш Windows Update сброшен не полностью: $($_.Exception.Message)"
        } finally {
            foreach ($service in @("cryptsvc", "bits", "wuauserv")) {
                Start-Service -Name $service -ErrorAction Continue
            }
        }
    }

    if ($CleanTemp) {
        Write-Step "Очистка старых временных файлов"
        $cutoff = (Get-Date).AddDays(-[Math]::Abs($TempOlderThanDays))
        $tempRoots = @(
            (Join-Path $env:SystemRoot "Temp"),
            $env:TEMP
        ) | Where-Object { $_ -and (Test-Path -LiteralPath $_) }

        foreach ($root in ($tempRoots | Select-Object -Unique)) {
            Write-Host "Очистка: $root"
            Get-ChildItem -LiteralPath $root -File -Recurse -Force -ErrorAction SilentlyContinue |
                Where-Object {
                    $_.LastWriteTime -lt $cutoff -and
                    -not ($_.Attributes -band [IO.FileAttributes]::ReparsePoint)
                } |
                Remove-Item -Force -ErrorAction SilentlyContinue

            Get-ChildItem -LiteralPath $root -Directory -Recurse -Force -ErrorAction SilentlyContinue |
                Where-Object {
                    $_.LastWriteTime -lt $cutoff -and
                    -not ($_.Attributes -band [IO.FileAttributes]::ReparsePoint)
                } |
                Sort-Object { $_.FullName.Length } -Descending |
                Where-Object { -not (Get-ChildItem -LiteralPath $_.FullName -Force -ErrorAction SilentlyContinue) } |
                Remove-Item -Force -ErrorAction SilentlyContinue
        }
    }

    Write-Step "Финальная быстрая проверка"
    dism.exe /Online /Cleanup-Image /CheckHealth
    Write-Host ""
    Write-Host "Лог сохранен: $transcriptPath"
    Write-Host "Рекомендация: перезагрузите ПК и проверьте Windows Update / Event Viewer."
} finally {
    Stop-Transcript | Out-Null
}

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

Что проверить перед внедрением

  • скрипт не запускается без прав администратора
  • лог сохраняется в отдельную папку на системном диске
  • временные файлы удаляются только если они старше заданного количества дней
  • сброс кэша Windows Update выключен по умолчанию и включается отдельным параметром

Итог: Windows проверена по шагам, результаты записаны в лог, а рискованные действия отделены параметрами и не выполняются молча.