From 2aaf44ad908b8ee48e1126a197cee01b6d12072a Mon Sep 17 00:00:00 2001 From: Petr Stepan Date: Sat, 6 Jun 2026 12:16:20 +0200 Subject: [PATCH] Pred vycistenim --- .DS_Store | Bin 8196 -> 8196 bytes Invoke-SecureBootRemediation.ps1 | 202 +++++++++++++++++++++++++------ 2 files changed, 167 insertions(+), 35 deletions(-) diff --git a/.DS_Store b/.DS_Store index 3c09b4dc2c57986ac7d019435fcd2b72367e211a..6a3842f3c375114c53d7e26cadf89a2551fa7305 100644 GIT binary patch delta 88 zcmZp1XmOa}FUrQiz`)4BAi%&-%82Hwr=94s6%AQkKkdJM@7 gISh%&s`7IfHuDHPVBgFx@r`BjR6(1~a-yf10F^WrwEzGB delta 67 zcmZp1XmOa}FUrcmz`)4BAi$7RUR;orlb;0S@7h>6jeTMR?`C!m77j+%&FTUVfL!U# V>=NHtCZ85m+-xa&j%i|p1OQ<|6RZFL diff --git a/Invoke-SecureBootRemediation.ps1 b/Invoke-SecureBootRemediation.ps1 index 456d725..64dbc81 100644 --- a/Invoke-SecureBootRemediation.ps1 +++ b/Invoke-SecureBootRemediation.ps1 @@ -112,25 +112,35 @@ $AU_BITS = @( @{ Bit=0x4000; Name='Conditional CA 2023 guard bit' } ) -# Symboly checklistu (z code-pointů kvůli odolnosti vůči kódování; lze změnit zde) -$SYM_DONE = [string][char]0x2713 # ✓ -$SYM_FAIL = [string][char]0x2717 # ✗ +# Značky checklistu. ASCII kvůli kompatibilitě — font Consolas (default konzole) nemá blok +# Dingbats, takže ✓/✗ by se zobrazily jako □. Chcete-li v Consolas „fajfku", nastavte +# $SYM_DONE = [string][char]0x221A (√, blok Mathematical Operators, Consolas ho má). +$SYM_DONE = '+' +$SYM_FAIL = '!' $SYM_PENDING = ' ' #endregion #region ── Log / barevný výstup ─────────────────────────────────────────────── -$scriptDir = if ($PSScriptRoot) { $PSScriptRoot } else { (Get-Location).Path } -$script:LogFile = if ($LogPath) { $LogPath } else { - Join-Path $scriptDir ("SecureBootRemediation-{0}-{1}.log" -f $env:COMPUTERNAME, (Get-Date -Format 'yyyyMMdd-HHmmss')) -} -$script:LogActive = [bool]$LogPath +$scriptDir = if ($PSScriptRoot) { $PSScriptRoot } else { (Get-Location).Path } +# Jeden společný log na LOKÁLNÍM disku serveru (%ProgramData%), připojovaný pod sebe přes běhy. +# Záměrně NE vedle skriptu — ten může být RDP-redirected disk, kde per-řádkový zápis vytváří +# poškozené (null) soubory. Řádky se bufferují a zapíšou jednorázově (Flush-Log), což je odolné. +$script:LogFile = if ($LogPath) { $LogPath } else { Join-Path $WORK_ROOT 'SecureBootRemediation.log' } +$script:LogBuffer = New-Object System.Collections.Generic.List[string] function Add-LogLine { param([string]$Text) - if (-not $script:LogActive) { return } - try { ('[{0}] {1}' -f (Get-Date -Format 'yyyy-MM-dd HH:mm:ss'), $Text) | Out-File -FilePath $script:LogFile -Append -Encoding UTF8 -Force } catch { } + [void]$script:LogBuffer.Add(('[{0}] {1}' -f (Get-Date -Format 'yyyy-MM-dd HH:mm:ss'), $Text)) +} +function Flush-Log { + if ($script:LogBuffer.Count -eq 0) { return } + try { + if (-not (Test-Path $WORK_ROOT)) { New-Item -Path $WORK_ROOT -ItemType Directory -Force | Out-Null } + [System.IO.File]::AppendAllText($script:LogFile, (($script:LogBuffer -join "`r`n") + "`r`n"), (New-Object System.Text.UTF8Encoding($false))) + $script:LogBuffer.Clear() + } catch { } } function Write-Line { param([string]$Text='', [string]$Color='Gray', [switch]$NoLog) Write-Host $Text -ForegroundColor $Color; if (-not $NoLog) { Add-LogLine $Text } } function Write-Rule { param([string]$Color='DarkCyan') Write-Host ('=' * 64) -ForegroundColor $Color } @@ -465,6 +475,16 @@ function Get-RemediationCategory { return @{ Code='FIRMWARE_UPDATE_NEEDED'; Tag='[FW]'; Color='Magenta'; Label='Aktualizace pozastavena (známý problém) — zkontrolujte firmware u OEM' } } + # Build příliš starý: DB/KEK už mají 2023, ale boot-manager servicing hodnoty vůbec neexistují + # (WindowsUEFICA2023Capable i UEFICA2023Status absentní) a Boot Manager není aktivní → OS nemá + # 2023-podepsaný boot manager ani jeho servicing. Restarty nepomohou, pomůže jen Windows Update. + $certApplied = $ph['Kek2023'].Done -or $ph['Db2023Windows'].Done + $auNow = [int]$reg.AvailableUpdates + $bmStaged = ([bool]$Result.BootManager.EspHas2023) -or ($auNow -eq 0x4100) -or ($auNow -eq 0x4000) + if ($certApplied -and -not $ph['BootManager2023'].Done -and -not $bmStaged -and ($null -eq $reg.WindowsUEFICA2023Capable) -and ($null -eq $reg.UEFICA2023Status)) { + return @{ Code='BUILD_OUTDATED'; Tag='[!]'; Color='Magenta'; Label='Build je příliš starý — chybí servicing pro Boot Manager 2023. Nutný Windows Update.' } + } + $req=@('Kek2023','Db2023Windows','BootManager2023') $missing=@($req|Where-Object{ -not $ph[$_].Done }); $missingLabels=@($missing|ForEach-Object{ $ph[$_].Label }) $anyApplied = $ph['Kek2023'].Done -or $ph['Db2023Windows'].Done -or $ph['BootManager2023'].Done -or $cert.DB.Has2023UEFI -or $cert.DB.Has2023OptionROM -or [bool]$Result.BootManager.EspHas2023 @@ -490,6 +510,7 @@ function Invoke-Detection { $R = [ordered]@{ AuditTimestamp=(Get-Date).ToString('yyyy-MM-dd HH:mm:ss'); Hostname=$env:COMPUTERNAME OSCaption=if($os){$os.Caption}else{$null}; OSBuild=if($os){$os.BuildNumber}else{$null} + OSBuildFull=$(try { $cv=Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion' -ErrorAction Stop; '{0}.{1}' -f $cv.CurrentBuildNumber,$cv.UBR } catch { if($os){[string]$os.BuildNumber}else{'?'} }) EnvironmentType=Get-EnvironmentType; Hardware=Get-HardwareInfo; SecureBoot=Get-SecureBootState OperatingMode='Unknown'; BitLocker=$null; Certificates=$null; Registry=Get-RegistryStatus EventLog=Get-EventLogStatus; TaskExists=Get-TaskExists; BootManager=$null; Phases=$null @@ -517,11 +538,11 @@ function Invoke-Detection { #region ── Výpis ────────────────────────────────────────────────────────────── function Show-DetectionSummary { - param($R) + param($R, $Prereqs, $Prev) $sb=$R.SecureBoot; $c=$R.Certificates; $reg=$R.Registry; $evt=$R.EventLog; $hw=$R.Hardware; $ph=$R.Phases; $bl=$R.BitLocker Write-Head 'SERVER' - Write-KV 'Stroj' $R.Hostname 'White' ("· {0} (build {1})" -f $R.OSCaption, $R.OSBuild) + Write-KV 'Stroj' $R.Hostname 'White' ("· {0} (build {1})" -f $R.OSCaption, $R.OSBuildFull) Write-KV 'Prostředí' $R.EnvironmentType 'White' ("· {0} {1}" -f $hw.Manufacturer, $hw.Model) if ($sb.IsUEFI -and $sb.IsSupported) { $modeColor = if ($R.OperatingMode -eq 'Setup') {'Red'} elseif ($R.OperatingMode -in @('User','Deployed')) {'White'} else {'Yellow'} @@ -533,10 +554,14 @@ function Show-DetectionSummary { Write-KV 'BitLocker' ("{0} [{1}]" -f $bl.Status, $bl.Protectors) $(if($blOn -and $bl.UsesPcr){'Yellow'}else{'White'}) $note $(if($blOn -and $bl.UsesPcr){'Yellow'}else{'DarkGray'}) } + if ($Prereqs) { Show-Prerequisites -Prereqs $Prereqs } + Write-Head 'POSTUP AKTUALIZACE (checklist)' if ($sb.IsUEFI -and $sb.IsSupported) { foreach ($k in $ph.Keys) { $p=$ph[$k]; Write-Check -State $p.State -Text $p.Label -Note $p.Note } } else { Write-Check -State 'Fail' -Text 'Secure Boot není podporováno / zapnuto' -Note 'remediace zde nedává smysl' } + Show-Progress -R $R -Prev $Prev + Write-Head 'STAV REGISTRŮ / FIRMWARE' Write-KV 'AvailableUpdates' (Get-AvailableUpdatesText $reg.AvailableUpdates) 'Cyan' Write-KV 'UEFICA2023Status' $reg.UEFICA2023StatusText $(if($reg.UEFICA2023StatusText -eq 'Updated'){'Green'}elseif($reg.UEFICA2023StatusText -eq 'Failed'){'Red'}else{'White'}) @@ -597,10 +622,19 @@ function Resolve-RemediationPlan { 'UPDATE_NEEDED' { $plan.Applicable=$true; $plan.AskUser=$true; $plan.Reason='Lze zahájit aktualizaci metodou registry (KB5068202).' } 'UPDATE_PARTIAL' { $plan.Applicable=$true; $plan.AskUser=$true; $plan.Reason='Část už nasazena, zbytek se aplikuje v dalším cyklu.'; $plan.Caution='Po nastavení a tasku bude potřeba RESTART; opakujte, dokud nebude HOTOVO.' } 'UPDATE_PENDING' { - $plan.Applicable=$true; $plan.AskUser=$true - $plan.Reason='KEK i DB hotové. Boot Manager se aktivuje až po RESTARTU.' - $plan.Caution='Nech znovu nastavit registry/task a pak RESTARTUJ.' - $plan.NextSteps=@('Po tomto kroku RESTARTUJTE server.','Po restartu spusťte kontrolu znovu (nebo -RegisterResume).') + $au = [int]$R.Registry.AvailableUpdates + # „Staged" = Boot Manager je už připravený k aktivaci (2023 na ESP, nebo AvailableUpdates + # v boot-manager fázi 0x4100/0x4000). Pak STAČÍ RESTART. Jinak je nutné ho teprve naplánovat. + $staged = ([bool]$R.BootManager.EspHas2023) -or ($au -eq 0x4100) -or ($au -eq 0x4000) + if ($staged) { + $plan.Reason='Boot Manager je připravený (staged). Aktivuje se SAMOTNÝM RESTARTEM — víc nastavovat netřeba.' + $plan.NextSteps=@('RESTARTUJTE server (stačí restart, nic dalšího nenastavujte).','BitLocker (PCR7): před restartem ověřte recovery key.','Po restartu spusťte kontrolu — měl by být zelený HOTOVO.') + } else { + $plan.Applicable=$true; $plan.AskUser=$true + $plan.Reason='KEK i DB hotové, ale Boot Manager ještě NENÍ připravený (AvailableUpdates=0x0, ESP není 2023). Je třeba nastavit registry + spustit task a teprve POTÉ restartovat.' + $plan.Caution='Tímto se naplánuje Boot Manager; aktivuje se až RESTARTEM (re-aplikace už hotových certifikátů je neškodná).' + $plan.NextSteps=@('Po tomto kroku RESTARTUJTE server.','Po restartu spusťte kontrolu znovu (nebo -RegisterResume).') + } } 'UPDATE_FAILED' { if ($R.EventLog.ById[1795]) { @@ -622,6 +656,10 @@ function Resolve-RemediationPlan { $plan.Reason='Secure Boot je v Setup Mode (chybí enrolled Platform Key) — aktualizaci nelze dokončit.' $plan.NextSteps=@('V UEFI/BIOS obnovte výchozí Secure Boot klíče (enroll PK) / přepněte do User Mode.','Poté spusťte kontrolu znovu.') } + 'BUILD_OUTDATED' { + $plan.Reason='OS má 2023 certifikáty v DB/KEK, ale chybí servicing pro aktivaci 2023 Boot Manageru (WindowsUEFICA2023Capable i UEFICA2023Status jsou prázdné). Boot Manager je stále podepsaný 2011 a tento build neumí nasadit nový — restarty ani remediace nepomohou.' + $plan.NextSteps=@('Nainstalujte nejnovější kumulativní update Windows (Windows Update) a restartujte.','Poté spusťte kontrolu znovu — pak by se Boot Manager měl dát aktualizovat.','Pozn.: velmi staré buildy (např. Server 2016 RTM) neobsahují 2023 boot-manager servicing.') + } 'TASK_MISSING' { $plan.Reason='Servicing task neexistuje — typicky chybí build z 10/2025 (KB5066835).' $plan.NextSteps=@('Nainstalujte nejnovější kumulativní update Windows.','Poté spusťte kontrolu znovu.') @@ -655,7 +693,7 @@ function Save-ResumeState { param($R, [int]$Cycle) try { if (-not (Test-Path $WORK_ROOT)) { New-Item -Path $WORK_ROOT -ItemType Directory -Force | Out-Null } - @{ ComputerName=$env:COMPUTERNAME; Timestamp=(Get-Date).ToString('o'); Cycle=$Cycle; Category=$R.Category + @{ ComputerName=$env:COMPUTERNAME; Timestamp=(Get-Date -Format 'yyyy-MM-dd HH:mm'); Cycle=$Cycle; Category=$R.Category AvailableUpdates=('0x{0:X}' -f [int]$R.Registry.AvailableUpdates) } | ConvertTo-Json | Set-Content -LiteralPath $STATE_FILE -Encoding UTF8 } catch { } } @@ -680,18 +718,30 @@ function Invoke-Remediation { $out=[ordered]@{ Status='Unknown'; Message=''; After=$null; LogFile=$script:LogFile } Write-Head 'REMEDIACE'; Add-LogLine 'REMEDIACE START' - Write-Host ' [1/3] Nastavuji registry (MicrosoftUpdateManagedOptIn=1, AvailableUpdates=0x5944) ... ' -NoNewline + # Přečti AvailableUpdates ještě PŘED zápisem — rozhodnutí, jestli ho nastavit nebo zachovat. + $auBefore = (Get-ItemProperty $REG_SECUREBOOT -ErrorAction SilentlyContinue).AvailableUpdates + # "In progress" = nenulová hodnota → systém ji spravuje sám (dle KB5068202 nastavit jen jednou). + # Null nebo 0 → první start nebo re-trigger (Boot Manager není aktivní přesto, že certifikáty jsou OK). + $auInProgress = ($null -ne $auBefore) -and ([int]$auBefore -ne 0) + $auLabel = if ($auInProgress) { '0x{0:X} zachováno (probíhá)' -f [int]$auBefore } else { '0x5944 (nové/reset)' } + Write-Host (" [1/3] Nastavuji registry (MicrosoftUpdateManagedOptIn=1, AvailableUpdates={0}) ... " -f $auLabel) -NoNewline try { if (-not (Test-Path $REG_SECUREBOOT)) { New-Item -Path $REG_SECUREBOOT -Force -ErrorAction Stop | Out-Null } Set-ItemProperty -Path $REG_SECUREBOOT -Name 'MicrosoftUpdateManagedOptIn' -Value 1 -Type DWord -Force -ErrorAction Stop - Set-ItemProperty -Path $REG_SECUREBOOT -Name 'AvailableUpdates' -Value $AVAILABLE_UPDATES_VALUE -Type DWord -Force -ErrorAction Stop - $o=Get-ItemProperty $REG_SECUREBOOT -Name 'HighConfidenceOptOut' -ErrorAction SilentlyContinue + # AvailableUpdates nastavujeme na 0x5944 POUZE pokud ještě nebyl nastaven (null) nebo je 0. + # Pokud je nenulový, hodnotu zachováme — resetování by smazalo již dosažený postup. + if (-not $auInProgress) { + Set-ItemProperty -Path $REG_SECUREBOOT -Name 'AvailableUpdates' -Value $AVAILABLE_UPDATES_VALUE -Type DWord -Force -ErrorAction Stop + } + $o = Get-ItemProperty $REG_SECUREBOOT -Name 'HighConfidenceOptOut' -ErrorAction SilentlyContinue if ($o -and $o.HighConfidenceOptOut -ne 0) { Set-ItemProperty -Path $REG_SECUREBOOT -Name 'HighConfidenceOptOut' -Value 0 -Type DWord -Force -ErrorAction Stop } if (-not (Test-Path $REG_SERVICING)) { New-Item -Path $REG_SERVICING -Force -ErrorAction Stop | Out-Null } - $v=(Get-ItemProperty $REG_SECUREBOOT -Name 'AvailableUpdates' -ErrorAction Stop).AvailableUpdates - if ($v -ne $AVAILABLE_UPDATES_VALUE) { throw "Ověření selhalo — AvailableUpdates=$v" } + $vNow = (Get-ItemProperty $REG_SECUREBOOT -ErrorAction Stop).AvailableUpdates + $vExpected = if ($auInProgress) { [int]$auBefore } else { $AVAILABLE_UPDATES_VALUE } + if ([int]$vNow -ne $vExpected) { throw ('Ověření selhalo — AvailableUpdates=0x{0:X}, očekáváno 0x{1:X}' -f [int]$vNow, $vExpected) } Write-Host 'hotovo' -ForegroundColor Green - Add-LogLine 'Krok 1: MicrosoftUpdateManagedOptIn=1, AvailableUpdates=0x5944 zapsáno a ověřeno' + $auAction = if ($auInProgress) { 'zachováno' } else { 'nastaveno' } + Add-LogLine ('Krok 1: MicrosoftUpdateManagedOptIn=1, AvailableUpdates=0x{0:X} ({1})' -f [int]$vNow, $auAction) } catch { Write-Host 'CHYBA' -ForegroundColor Red; Write-Line (" {0}" -f $_.Exception.Message) Red; $out.Status='Error'; $out.Message="Zápis registry selhal: $($_.Exception.Message)"; return $out } if ($SkipScheduledTask) { Write-Line ' [2/3] Servicing task přeskočen (-SkipScheduledTask).' DarkGray } @@ -731,6 +781,81 @@ function Invoke-Remediation { #endregion +#region ── Předpoklady + progress ──────────────────────────────────────────── + +function Get-StageInfo { + param($v) + if ($null -eq $v) { return @{ Step=0; Total=6; Label='nezahájeno' } } + switch ([int]$v) { + 0x5944 { return @{ Step=1; Total=6; Label='naplánováno (start)' } } + 0x5904 { return @{ Step=2; Total=6; Label='Windows UEFI CA 2023 v DB' } } + 0x5104 { return @{ Step=3; Total=6; Label='Option ROM UEFI CA 2023 v DB' } } + 0x4104 { return @{ Step=4; Total=6; Label='Microsoft UEFI CA 2023 v DB' } } + 0x4100 { return @{ Step=5; Total=6; Label='Boot Manager nasazen na ESP' } } + 0x4000 { return @{ Step=6; Total=6; Label='čeká na finální restart' } } + 0 { return @{ Step=6; Total=6; Label='dokončeno' } } + default { return @{ Step=1; Total=6; Label=('mezistav 0x{0:X}' -f [int]$v) } } + } +} + +function Show-Progress { + param($R, $Prev) + if ($R.Category -notlike 'UPDATE*' -and $R.Category -notlike 'OK*') { return } + Write-Head 'PRŮBĚH (krok a změna od minulého běhu)' + $st = Get-StageInfo $R.Registry.AvailableUpdates + $bar = ('#' * $st.Step) + ('.' * ($st.Total - $st.Step)) + Write-Host (" [{0}] krok {1}/{2} — {3}" -f $bar, $st.Step, $st.Total, $st.Label) -ForegroundColor Cyan + if ($Prev) { + $pv = [string]$Prev.AvailableUpdates + $cv = '0x{0:X}' -f [int]$R.Registry.AvailableUpdates + if ($pv -eq $cv) { + Write-Host (" Beze změny od minulého běhu (#{0}, {1})." -f $Prev.Cycle, $Prev.Timestamp) -ForegroundColor Yellow + if ($R.Category -in @('UPDATE_PENDING','UPDATE_PARTIAL')) { + Write-Host ' -> Proces čeká na RESTART. Pokud jste restartoval, spusťte skript znovu; jinak server restartujte.' -ForegroundColor Yellow + } + } else { + Write-Host (" Od minulého běhu: AvailableUpdates {0} -> {1} (postup)" -f $pv, $cv) -ForegroundColor Green + } + } +} + +function Get-Prerequisites { + param($R, [bool]$IsAdmin) + $sb = $R.SecureBoot + $list = @() + $list += @{ Label='Spuštěno jako Administrator'; Ok=$IsAdmin; Hard=$true; Note=$(if(-not $IsAdmin){'spusťte PowerShell „Run as administrator"'}) } + $list += @{ Label='UEFI + Secure Boot podporováno'; Ok=($sb.IsUEFI -and $sb.IsSupported); Hard=$true; Note=$(if(-not ($sb.IsUEFI -and $sb.IsSupported)){'Legacy BIOS / nepodporováno'}) } + $list += @{ Label='Secure Boot zapnutý'; Ok=[bool]$sb.IsEnabled; Hard=$true; Note=$(if($sb.IsUEFI -and $sb.IsSupported -and -not $sb.IsEnabled){'zapněte v UEFI'}) } + $list += @{ Label='Ne Setup Mode (enrolled PK)'; Ok=($R.OperatingMode -ne 'Setup'); Hard=$true; Note=$(if($R.OperatingMode -eq 'Setup'){'obnovte Secure Boot klíče v UEFI'}) } + $buildNote = if ($R.TaskExists) { "build $($R.OSBuildFull)" } else { "build $($R.OSBuildFull) — chybí task, nainstalujte 10/2025+ (KB5066835)" } + $list += @{ Label='Build s podporou aktualizace (servicing task)'; Ok=[bool]$R.TaskExists; Hard=$true; Note=$buildNote } + # Boot-manager servicing přítomný? Když jsou certy v DB/KEK, ale WindowsUEFICA2023Capable i + # UEFICA2023Status chybí a boot manager není aktivní → build je příliš starý (nutný Windows Update). + $auP = [int]$R.Registry.AvailableUpdates + $bmStagedP = ([bool]$R.BootManager.EspHas2023) -or ($auP -eq 0x4100) -or ($auP -eq 0x4000) + $staleServ = ($R.Phases['Kek2023'].Done -or $R.Phases['Db2023Windows'].Done) -and (-not $R.Phases['BootManager2023'].Done) -and (-not $bmStagedP) -and ($null -eq $R.Registry.WindowsUEFICA2023Capable) -and ($null -eq $R.Registry.UEFICA2023Status) + $list += @{ Label='Servicing pro Boot Manager 2023 (aktuální build)'; Ok=(-not $staleServ); Hard=$true; Note=$(if($staleServ){"build $($R.OSBuildFull) je starý — chybí 2023 boot-manager servicing, nutný Windows Update"}) } + $list += @{ Label='Zařízení není „Not Supported"'; Ok=($R.Registry.ConfidenceLevel -notlike 'Not Supported*'); Hard=$false; Note=$(if($R.Registry.ConfidenceLevel -like 'Not Supported*'){'ConfidenceLevel: Not Supported — řešení u OEM'}) } + return $list +} + +function Show-Prerequisites { + param($Prereqs) + Write-Head 'PŘEDPOKLADY (lze proces dokončit?)' + foreach ($p in $Prereqs) { + $state = if ($p.Ok) { 'Done' } elseif ($p.Hard) { 'Fail' } else { 'Info' } + $nc = if ($p.Ok) { 'DarkGray' } elseif ($p.Hard) { 'Red' } else { 'Yellow' } + Write-Check -State $state -Text $p.Label -Note $p.Note -NoteColor $nc + } + if (@($Prereqs | Where-Object { $_.Hard -and -not $_.Ok }).Count) { + Write-Host ' ! Než spustíte aktualizaci, vyřešte výše označené řádky — jinak proces nelze dokončit.' -ForegroundColor Red + } else { + Write-Host ' Předpoklady splněny — jakmile spustíte, proces lze dokončit.' -ForegroundColor Green + } +} + +#endregion + #region ── Main ─────────────────────────────────────────────────────────────── $isWhatIf = [bool]$WhatIfPreference @@ -742,25 +867,27 @@ Write-Host (" {0} {1}" -f $env:COMPUTERNAME, (Get-Date -Format 'yyyy-MM-dd HH Write-Rule 'Cyan' $isAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator) +Add-LogLine ('===== SPUŠTĚNO {0} | {1} | admin={2} | CheckOnly={3} AssumeYes={4} Force={5} =====' -f $env:COMPUTERNAME, (Get-Date -Format 'yyyy-MM-dd HH:mm:ss'), $isAdmin, $CheckOnly.IsPresent, $AssumeYes.IsPresent, $Force.IsPresent) if (-not $isAdmin) { Write-Host ''; Write-Host ' ! Neběží jako Administrator — část kontrol a remediace nebude dostupná.' -ForegroundColor Yellow } $prev = Get-ResumeState if ($prev) { Write-Host ''; Write-Host (" Navazuji na předchozí běh (cyklus #{0}, {1}, stav {2})." -f $prev.Cycle, $prev.Timestamp, $prev.Category) -ForegroundColor DarkCyan } Write-Host ''; Write-Host ' Zjišťuji stav (vč. ověření Boot Manageru)…' -ForegroundColor DarkGray -$result = Invoke-Detection -SkipBootMgrFile:$SkipBootManagerFileCheck.IsPresent -Show-DetectionSummary -R $result +$result = Invoke-Detection -SkipBootMgrFile:$SkipBootManagerFileCheck.IsPresent +$prereqs = Get-Prerequisites -R $result -IsAdmin $isAdmin +# Pořadí: SERVER -> PŘEDPOKLADY -> POSTUP AKTUALIZACE (checklist) -> PRŮBĚH -> registry -> VÝSLEDEK +Show-DetectionSummary -R $result -Prereqs $prereqs -Prev $prev if ($Detailed) { Show-DetailedDetection -R $result } -if ($result.SecureBoot.IsEnabled -and -not $result.Registry.ServicingKeyExists) { - Write-Host ''; Write-Host ' ! Chybí klíč SecureBoot\Servicing — pravděpodobně build < 10/2025. Nainstalujte KB5066835+.' -ForegroundColor Yellow -} - if ($CheckOnly) { Write-Host ''; Write-Rule $code = switch -Wildcard ($result.Category) { 'OK*' { 0 } 'UPDATE_NEEDED' { 1 } 'UPDATE_PARTIAL' { 1 } 'UPDATE_PENDING' { 1 } default { 2 } } Write-Host (" CHECK: {0} (exit {1})" -f $result.CategoryLabel, $code) -ForegroundColor $result.CategoryColor + Write-Host (" Log: {0}" -f $script:LogFile) -ForegroundColor DarkGray + Save-ResumeState -R $result -Cycle $(if ($prev) { [int]$prev.Cycle } else { 0 }) + Flush-Log if ($PassThru) { $result } exit $code } @@ -769,10 +896,11 @@ $plan = Resolve-RemediationPlan -R $result -ForceMode:$Force.IsPresent Write-Head 'VYHODNOCENÍ' if ($plan.Reason) { Write-Host (" {0}" -f $plan.Reason) -ForegroundColor Gray } +$cycle = if ($prev) { [int]$prev.Cycle } else { 0 } $remediation = $null if (-not $plan.Applicable) { if ($plan.NextSteps.Count) { Write-Host ''; Write-Host ' Další kroky:' -ForegroundColor White; $i=1; foreach ($s in $plan.NextSteps) { Write-Host (" {0}. {1}" -f $i,$s) -ForegroundColor Gray; $i++ } } - elseif ($result.Category -like 'OK*') { Write-Host ' Není potřeba žádná akce.' -ForegroundColor Green; Clear-ResumeState } + elseif ($result.Category -like 'OK*') { Write-Host ' Není potřeba žádná akce.' -ForegroundColor Green } } else { if ($plan.Caution) { Write-Host (" Pozor: {0}" -f $plan.Caution) -ForegroundColor Yellow } if (-not $isAdmin) { Write-Host ''; Write-Host ' Remediaci nelze provést bez práv Administrator.' -ForegroundColor Yellow } @@ -785,11 +913,10 @@ if (-not $plan.Applicable) { $q='Chcete nyní zahájit / pokračovat v aktualizaci Secure Boot certifikátů?' $d='Nastaví registry (KB5068202) a spustí servicing task. Server NEBUDE restartován.' if (Get-UserConsent -Question $q -Detail $d) { - $script:LogActive=$true - $cycle = if ($prev) { [int]$prev.Cycle + 1 } else { 1 } + $cycle = $cycle + 1 Add-LogLine ("Cyklus #{0} | {1} | kategorie={2}" -f $cycle, $result.Hostname, $result.Category) $remediation = Invoke-Remediation -SkipBootMgrFile:$SkipBootManagerFileCheck.IsPresent - if ($remediation.After) { Save-ResumeState -R $remediation.After -Cycle $cycle } + Flush-Log } else { Write-Host ''; Write-Host ' Remediace neprovedena (volba uživatele).' -ForegroundColor Yellow } } } @@ -797,7 +924,7 @@ if (-not $plan.Applicable) { Write-Host ''; Write-Rule 'Cyan' if ($remediation -and $remediation.Status -eq 'Applied') { $after=$remediation.After - if ($after.Category -like 'OK*') { Write-Host (" {0} HOTOVO — server je kompletní." -f $SYM_DONE) -ForegroundColor Green; Clear-ResumeState } + if ($after.Category -like 'OK*') { Write-Host (" {0} HOTOVO — server je kompletní." -f $SYM_DONE) -ForegroundColor Green } else { Write-Host ' ČÁSTEČNĚ HOTOVO — proces pokračuje po restartu.' -ForegroundColor Yellow Write-Host ''; Write-Host ' Další kroky:' -ForegroundColor White @@ -809,11 +936,16 @@ if ($remediation -and $remediation.Status -eq 'Applied') { Write-Host ' Cíl: AvailableUpdates=0x0 · UEFICA2023Status=Updated · WinUEFICA2023Capable=2.' -ForegroundColor DarkGray if ($RegisterResume) { Register-Resume } else { Write-Host ' (Tip: -RegisterResume spustí kontrolu po příštím restartu automaticky.)' -ForegroundColor DarkGray } } - Write-Host ''; Write-Host (" Log: {0}" -f $script:LogFile) -ForegroundColor DarkGray } elseif ($remediation -and $remediation.Status -eq 'Error') { Write-Host (' CHYBA REMEDIACE — {0}' -f $remediation.Message) -ForegroundColor Red } else { Write-Host ' KONEC — bez změn na serveru.' -ForegroundColor Cyan } +Write-Host (" Log: {0}" -f $script:LogFile) -ForegroundColor DarkGray Write-Rule 'Cyan'; Write-Host '' +# Stav pro příští běh (delta) + jednorázový zápis logu +$finalR = if ($remediation -and $remediation.After) { $remediation.After } else { $result } +if ($finalR.Category -like 'OK*') { Clear-ResumeState } else { Save-ResumeState -R $finalR -Cycle $cycle } +Flush-Log + if ($PassThru) { return [pscustomobject]@{ Detection=$result; Plan=$plan; Remediation=$remediation } } #endregion