Replace unicode chars with Powershell 5.1 compatible chars

This commit is contained in:
Petr Stepan
2026-07-17 11:58:04 +02:00
parent d7664f0dbb
commit 58ff50cecd
+142 -140
View File
@@ -12,7 +12,7 @@
script skončí s informací, že není nutná žádná akce. script skončí s informací, že není nutná žádná akce.
Výjimka: parametr -Force umožní remediaci přesto spustit. Výjimka: parametr -Force umožní remediaci přesto spustit.
Kritéria úspěšné remediace (post-remediation všechny 4 podmínky): Kritéria úspěšné remediace (post-remediation - všechny 4 podmínky):
1. Windows UEFI CA 2023 v DB + Microsoft Corporation KEK 2K CA 2023 v KEK 1. Windows UEFI CA 2023 v DB + Microsoft Corporation KEK 2K CA 2023 v KEK
2. AvailableUpdates = 0x0 nebo 0x4000 2. AvailableUpdates = 0x0 nebo 0x4000
3. UEFICA2023Status = "Updated" 3. UEFICA2023Status = "Updated"
@@ -62,7 +62,7 @@ try {
$OutputEncoding = $utf8NoBom $OutputEncoding = $utf8NoBom
} catch { } } catch { }
#region ── Konstanty ────────────────────────────────────────────────────────── #region -- Konstanty ----------------------------------------------------------
$REG_SECUREBOOT = 'HKLM:\SYSTEM\CurrentControlSet\Control\SecureBoot' $REG_SECUREBOOT = 'HKLM:\SYSTEM\CurrentControlSet\Control\SecureBoot'
$REG_SERVICING = 'HKLM:\SYSTEM\CurrentControlSet\Control\SecureBoot\Servicing' $REG_SERVICING = 'HKLM:\SYSTEM\CurrentControlSet\Control\SecureBoot\Servicing'
@@ -85,19 +85,19 @@ $SYM_PENDING = ' '
# Popisy EventID pro sekci UDÁLOSTI # Popisy EventID pro sekci UDÁLOSTI
$EVENT_DESC = @{ $EVENT_DESC = @{
1795 = 'Chyba firmwaru aktualizace selhala' 1795 = 'Chyba firmwaru - aktualizace selhala'
1796 = 'Selhání zápisu UEFI proměnné' 1796 = 'Selhání zápisu UEFI proměnné'
1799 = 'Informační: aktualizace dokončena' 1799 = 'Informační: aktualizace dokončena'
1800 = 'Aktualizace zahájena' 1800 = 'Aktualizace zahájena'
1801 = 'Čeká na podmínky (monitor)' 1801 = 'Čeká na podmínky (monitor)'
1802 = 'Pozastaveno problém firmwaru' 1802 = 'Pozastaveno - problém firmwaru'
1803 = 'Odloženo (monitorovací podmínka)' 1803 = 'Odloženo (monitorovací podmínka)'
1808 = 'Certifikáty úspěšně aplikovány' 1808 = 'Certifikáty úspěšně aplikovány'
} }
#endregion #endregion
#region ── Log / barevný výstup ─────────────────────────────────────────────── #region -- Log / barevný výstup -----------------------------------------------
$script:LogFile = Join-Path $WORK_ROOT 'SecureBootRemediation.log' $script:LogFile = Join-Path $WORK_ROOT 'SecureBootRemediation.log'
$script:LogBuffer = New-Object System.Collections.Generic.List[string] $script:LogBuffer = New-Object System.Collections.Generic.List[string]
@@ -173,7 +173,7 @@ function Write-Check {
#endregion #endregion
#region ── Detekce prostředí / HW / Secure Boot ──────────────────────────── #region -- Detekce - prostředí / HW / Secure Boot ----------------------------
function Get-EnvironmentType { function Get-EnvironmentType {
try { try {
@@ -268,7 +268,7 @@ function Get-BitLockerInfo {
#endregion #endregion
#region ── Detekce certifikáty / registry / události ─────────────────────── #region -- Detekce - certifikáty / registry / události -----------------------
function Parse-EFISignatureList { function Parse-EFISignatureList {
# Parsuje EFI_SIGNATURE_LIST a vrátí pole X509Certificate2 (pouze X.509 položky). # Parsuje EFI_SIGNATURE_LIST a vrátí pole X509Certificate2 (pouze X.509 položky).
@@ -386,7 +386,7 @@ function Get-CertificateStatus {
} }
} catch { $st.DB.Error = $_.Exception.Message } } catch { $st.DB.Error = $_.Exception.Message }
# ASCII fallback doplní booleany pokud X.509 parse selhal # ASCII fallback - doplní booleany pokud X.509 parse selhal
$kAscii = Get-SbVarAscii KEK $kAscii = Get-SbVarAscii KEK
if ($kAscii -and $kAscii -match [regex]::Escape($CN_KEK2023)) { $st.KEK.Has2023 = $true } if ($kAscii -and $kAscii -match [regex]::Escape($CN_KEK2023)) { $st.KEK.Has2023 = $true }
@@ -449,9 +449,9 @@ function Get-RegistryStatus {
} }
$reg.WindowsUEFICA2023CapableText = switch ($reg.WindowsUEFICA2023Capable) { $reg.WindowsUEFICA2023CapableText = switch ($reg.WindowsUEFICA2023Capable) {
0 { '0 Windows UEFI CA 2023 není v DB' } 0 { '0 - Windows UEFI CA 2023 není v DB' }
1 { '1 certifikát v DB (boot manager zatím ne)' } 1 { '1 - certifikát v DB (boot manager zatím ne)' }
2 { '2 bootuje se z 2023 boot manageru' } 2 { '2 - bootuje se z 2023 boot manageru' }
$null { '-' } $null { '-' }
default { [string]$reg.WindowsUEFICA2023Capable } default { [string]$reg.WindowsUEFICA2023Capable }
} }
@@ -466,8 +466,8 @@ function Get-AvailableUpdatesText {
$hex = '0x{0:X}' -f $iv $hex = '0x{0:X}' -f $iv
$name = switch ($iv) { $name = switch ($iv) {
0 { 'vše hotovo (proces dokončen)' } 0 { 'vše hotovo (proces dokončen)' }
0x4000 { 'vše aplikováno task čistí guard bit' } 0x4000 { 'vše aplikováno - task čistí guard bit' }
0x4100 { 'Boot Manager 2023 nasazen na ESP čeká RESTART' } 0x4100 { 'Boot Manager 2023 nasazen na ESP - čeká RESTART' }
0x4104 { 'Microsoft UEFI CA 2023 v DB' } 0x4104 { 'Microsoft UEFI CA 2023 v DB' }
0x5104 { 'Option ROM UEFI CA 2023 v DB' } 0x5104 { 'Option ROM UEFI CA 2023 v DB' }
0x5904 { 'Windows UEFI CA 2023 v DB' } 0x5904 { 'Windows UEFI CA 2023 v DB' }
@@ -520,7 +520,7 @@ function Get-TaskExists {
#endregion #endregion
#region ── Kategorizace ─────────────────────────────────────────────────────── #region -- Kategorizace -------------------------------------------------------
function Test-RemediationComplete { function Test-RemediationComplete {
# Ověří všechny podmínky úspěchu: 4 registry + ověření bootloaderu. # Ověří všechny podmínky úspěchu: 4 registry + ověření bootloaderu.
@@ -547,14 +547,14 @@ function Get-RemediationCategory {
$env = $Result.EnvironmentType $env = $Result.EnvironmentType
$mode = $Result.OperatingMode $mode = $Result.OperatingMode
# ── Základní blokátory ──────────────────────────────────────────────────── # -- Základní blokátory ----------------------------------------------------
if (-not $sb.IsUEFI -or -not $sb.IsSupported) { if (-not $sb.IsUEFI -or -not $sb.IsSupported) {
if ($env -like '*VM*') { if ($env -like '*VM*') {
return @{ Code='NO_SECUREBOOT_VM'; Color='DarkGray' return @{ Code='NO_SECUREBOOT_VM'; Color='DarkGray'
Label='Secure Boot nepodporováno VM bez vTPM nebo UEFI' } Label='Secure Boot nepodporováno - VM bez vTPM nebo UEFI' }
} }
return @{ Code='NO_SECUREBOOT'; Color='DarkGray' return @{ Code='NO_SECUREBOOT'; Color='DarkGray'
Label='Secure Boot nepodporováno Legacy BIOS' } Label='Secure Boot nepodporováno - Legacy BIOS' }
} }
if (-not $sb.IsEnabled) { if (-not $sb.IsEnabled) {
return @{ Code='SECUREBOOT_DISABLED'; Color='Yellow' return @{ Code='SECUREBOOT_DISABLED'; Color='Yellow'
@@ -562,18 +562,18 @@ function Get-RemediationCategory {
} }
if ($mode -eq 'Setup') { if ($mode -eq 'Setup') {
return @{ Code='SETUP_MODE'; Color='Magenta' return @{ Code='SETUP_MODE'; Color='Magenta'
Label='Secure Boot v Setup Mode aktualizaci nelze dokončit (chybí enrolled PK)' } Label='Secure Boot v Setup Mode - aktualizaci nelze dokončit (chybí enrolled PK)' }
} }
if (-not $Result.TaskExists) { if (-not $Result.TaskExists) {
return @{ Code='TASK_MISSING'; Color='Magenta' return @{ Code='TASK_MISSING'; Color='Magenta'
Label='Chybí servicing task nainstalujte kumulativní update min. z 10/2025 (KB5066835)' } Label='Chybí servicing task - nainstalujte kumulativní update min. z 10/2025 (KB5066835)' }
} }
# ── Chyby a blokující stavy ─────────────────────────────────────────────── # -- Chyby a blokující stavy -----------------------------------------------
$hasErrorEvent = $reg.UEFICA2023ErrorEvent -and ([int]$reg.UEFICA2023ErrorEvent -ne 0) $hasErrorEvent = $reg.UEFICA2023ErrorEvent -and ([int]$reg.UEFICA2023ErrorEvent -ne 0)
if ($evt.ById[1795] -or $hasErrorEvent -or $reg.UEFICA2023StatusText -eq 'Failed') { if ($evt.ById[1795] -or $hasErrorEvent -or $reg.UEFICA2023StatusText -eq 'Failed') {
return @{ Code='UPDATE_FAILED'; Color='Red' return @{ Code='UPDATE_FAILED'; Color='Red'
Label='Selhání aktualizace chyba firmwaru nebo UEFICA2023ErrorEvent' } Label='Selhání aktualizace - chyba firmwaru nebo UEFICA2023ErrorEvent' }
} }
if ($reg.ConfidenceLevel -like 'Not Supported*') { if ($reg.ConfidenceLevel -like 'Not Supported*') {
return @{ Code='NOT_SUPPORTED'; Color='Red' return @{ Code='NOT_SUPPORTED'; Color='Red'
@@ -581,17 +581,17 @@ function Get-RemediationCategory {
} }
if ($reg.ConfidenceLevel -like 'Temporarily Paused*' -or $evt.ById[1802]) { if ($reg.ConfidenceLevel -like 'Temporarily Paused*' -or $evt.ById[1802]) {
return @{ Code='FIRMWARE_UPDATE_NEEDED'; Color='Magenta' return @{ Code='FIRMWARE_UPDATE_NEEDED'; Color='Magenta'
Label='Aktualizace pozastavena problém firmwaru, zkontrolujte update u OEM' } Label='Aktualizace pozastavena - problém firmwaru, zkontrolujte update u OEM' }
} }
# ── Hotovo (registry + bootloader ověřen) ───────────────────────────────── # -- Hotovo (registry + bootloader ověřen) ---------------------------------
if (Test-RemediationComplete -R $Result) { if (Test-RemediationComplete -R $Result) {
$bm = $Result.BootManager $bm = $Result.BootManager
$bmReason = if ($reg.WindowsUEFICA2023Capable -eq 2) { 'Capable=2' } ` $bmReason = if ($reg.WindowsUEFICA2023Capable -eq 2) { 'Capable=2' } `
elseif ($bm -and $bm.Checked -and $bm.Is2023) { 'soubor ověřen' } ` elseif ($bm -and $bm.Checked -and $bm.Is2023) { 'soubor ověřen' } `
else { '' } else { '' }
$doneLabel = if ($bmReason) { "HOTOVO 2023 certifikáty + bootloader ověřen ($bmReason)" } ` $doneLabel = if ($bmReason) { "HOTOVO - 2023 certifikáty + bootloader ověřen ($bmReason)" } `
else { 'HOTOVO 2023 certifikáty nasazeny' } else { 'HOTOVO - 2023 certifikáty nasazeny' }
if ($cert.AnyExpiring2011) { if ($cert.AnyExpiring2011) {
return @{ Code='OK_TRANSITION'; Color='Green' return @{ Code='OK_TRANSITION'; Color='Green'
Label="$doneLabel (staré 2011 ještě přítomné, je to normální)" } Label="$doneLabel (staré 2011 ještě přítomné, je to normální)" }
@@ -599,10 +599,10 @@ function Get-RemediationCategory {
return @{ Code='OK'; Color='Green'; Label=$doneLabel } return @{ Code='OK'; Color='Green'; Label=$doneLabel }
} }
# ── Boot Manager staged, čeká na restart ───────────────────────────────── # -- Boot Manager staged, čeká na restart ---------------------------------
if ([int]$reg.AvailableUpdates -eq 0x4100) { if ([int]$reg.AvailableUpdates -eq 0x4100) {
return @{ Code='UPDATE_PENDING_RESTART'; Color='Yellow' return @{ Code='UPDATE_PENDING_RESTART'; Color='Yellow'
Label='Boot Manager 2023 nasazen (staged) čeká na RESTART' } Label='Boot Manager 2023 nasazen (staged) - čeká na RESTART' }
} }
$certsRequiredOk = $cert.KEK.Has2023 -and $cert.DB.Has2023WindowsUEFI $certsRequiredOk = $cert.KEK.Has2023 -and $cert.DB.Has2023WindowsUEFI
@@ -611,14 +611,14 @@ function Get-RemediationCategory {
$bm = $Result.BootManager $bm = $Result.BootManager
$bmOk = ($reg.WindowsUEFICA2023Capable -eq 2) -or ($bm -and $bm.Checked -and $bm.Is2023) $bmOk = ($reg.WindowsUEFICA2023Capable -eq 2) -or ($bm -and $bm.Checked -and $bm.Is2023)
# ── Certy přítomny + AU=0x0 + bootloader ověřen hotovo ───────────────── # -- Certy přítomny + AU=0x0 + bootloader ověřen -> hotovo -----------------
# Poznámka: null AU (chybějící Servicing klíč) se neuznává jako hotovo. # Poznámka: null AU (chybějící Servicing klíč) se neuznává jako hotovo.
if ($certsRequiredOk -and ($null -ne $reg.AvailableUpdates) -and $auNow -eq 0 -and $errOk -and $bmOk) { if ($certsRequiredOk -and ($null -ne $reg.AvailableUpdates) -and $auNow -eq 0 -and $errOk -and $bmOk) {
$bmReason = if ($reg.WindowsUEFICA2023Capable -eq 2) { 'Capable=2' } ` $bmReason = if ($reg.WindowsUEFICA2023Capable -eq 2) { 'Capable=2' } `
elseif ($bm -and $bm.Checked -and $bm.Is2023) { 'soubor ověřen' } ` elseif ($bm -and $bm.Checked -and $bm.Is2023) { 'soubor ověřen' } `
else { '' } else { '' }
$doneLabel = if ($bmReason) { "Certifikáty aplikovány bootloader ověřen ($bmReason)" } ` $doneLabel = if ($bmReason) { "Certifikáty aplikovány - bootloader ověřen ($bmReason)" } `
else { 'Certifikáty aplikovány AvailableUpdates=0x0' } else { 'Certifikáty aplikovány - AvailableUpdates=0x0' }
if ($cert.AnyExpiring2011) { if ($cert.AnyExpiring2011) {
return @{ Code='OK_TRANSITION'; Color='Green' return @{ Code='OK_TRANSITION'; Color='Green'
Label="$doneLabel (staré 2011 ještě přítomné)" } Label="$doneLabel (staré 2011 ještě přítomné)" }
@@ -626,8 +626,8 @@ function Get-RemediationCategory {
return @{ Code='OK'; Color='Green'; Label=$doneLabel } return @{ Code='OK'; Color='Green'; Label=$doneLabel }
} }
# ── Certy přítomny, AU=0x0, ale bootloader dosud nepoužívá 2023 CA ─────── # -- Certy přítomny, AU=0x0, ale bootloader dosud nepoužívá 2023 CA -------
# Systém potřebuje spustit task znovu staged BM 2023 restart. # Systém potřebuje spustit task znovu -> staged BM 2023 -> restart.
if ($certsRequiredOk -and ($null -ne $reg.AvailableUpdates) -and $auNow -eq 0 -and $errOk -and -not $bmOk) { if ($certsRequiredOk -and ($null -ne $reg.AvailableUpdates) -and $auNow -eq 0 -and $errOk -and -not $bmOk) {
$bmNote = if ($bm -and $bm.Checked -and $bm.Is2011) { $bmNote = if ($bm -and $bm.Checked -and $bm.Is2011) {
"soubor podepsán starší CA: $($bm.IssuerCA)" "soubor podepsán starší CA: $($bm.IssuerCA)"
@@ -637,37 +637,37 @@ function Get-RemediationCategory {
"Capable=$($reg.WindowsUEFICA2023Capable)" "Capable=$($reg.WindowsUEFICA2023Capable)"
} }
return @{ Code='UPDATE_BOOTMANAGER'; Color='Yellow' return @{ Code='UPDATE_BOOTMANAGER'; Color='Yellow'
Label="Certifikáty v DB/KEK bootloader dosud nepoužívá 2023 CA ($bmNote)" } Label="Certifikáty v DB/KEK - bootloader dosud nepoužívá 2023 CA ($bmNote)" }
} }
# ── Starý build: certy přítomny, ale servicing infrastruktura chybí ────── # -- Starý build: certy přítomny, ale servicing infrastruktura chybí ------
$certsPresent = $cert.KEK.Has2023 -or $cert.DB.Has2023WindowsUEFI $certsPresent = $cert.KEK.Has2023 -or $cert.DB.Has2023WindowsUEFI
$bmStagedAU = ($auNow -eq 0x4100 -or $auNow -eq 0x4000) $bmStagedAU = ($auNow -eq 0x4100 -or $auNow -eq 0x4000)
if ($certsPresent -and -not $bmStagedAU ` if ($certsPresent -and -not $bmStagedAU `
-and ($null -eq $reg.WindowsUEFICA2023Capable) ` -and ($null -eq $reg.WindowsUEFICA2023Capable) `
-and ($null -eq $reg.UEFICA2023Status)) { -and ($null -eq $reg.UEFICA2023Status)) {
return @{ Code='BUILD_OUTDATED'; Color='Magenta' return @{ Code='BUILD_OUTDATED'; Color='Magenta'
Label='Build příliš starý chybí servicing pro Boot Manager 2023, nutný Windows Update' } Label='Build příliš starý - chybí servicing pro Boot Manager 2023, nutný Windows Update' }
} }
# ── Částečný postup / nutná aktualizace ────────────────────────────────── # -- Částečný postup / nutná aktualizace ----------------------------------
if ($certsPresent) { if ($certsPresent) {
return @{ Code='UPDATE_PARTIAL'; Color='Cyan' return @{ Code='UPDATE_PARTIAL'; Color='Cyan'
Label='Aktualizace probíhá certifikáty nasazeny, zbývá dokončit servisování' } Label='Aktualizace probíhá - certifikáty nasazeny, zbývá dokončit servisování' }
} }
return @{ Code='UPDATE_NEEDED'; Color='Yellow' return @{ Code='UPDATE_NEEDED'; Color='Yellow'
Label='Povinné certifikáty 2023 nejsou přítomny nutná aktualizace' } Label='Povinné certifikáty 2023 nejsou přítomny - nutná aktualizace' }
} }
#endregion #endregion
#region ── Detekce bootloaderu ──────────────────────────────────────────────── #region -- Detekce bootloaderu ------------------------------------------------
function Get-EspBootmgfwCopy { function Get-EspBootmgfwCopy {
# Zkopíruje bootmgfw.efi z aktivního ESP do %TEMP% a vrátí výsledek. # Zkopíruje bootmgfw.efi z aktivního ESP do %TEMP% a vrátí výsledek.
# Logika převzata z referenčního scriptu (mathisokle/SecureBoot-CA2023-Automatic-Update). # Logika převzata z referenčního scriptu (mathisokle/SecureBoot-CA2023-Automatic-Update).
# Nikdy nepracuje přímo s live souborem vždy nejprve kopie. # Nikdy nepracuje přímo s live souborem - vždy nejprve kopie.
$out = @{ Success=$false; TempPath=$null; Source=$null; Error=$null } $out = @{ Success=$false; TempPath=$null; Source=$null; Error=$null }
$tempDir = Join-Path $env:TEMP 'SecureBootCA2023' $tempDir = Join-Path $env:TEMP 'SecureBootCA2023'
@@ -679,7 +679,7 @@ function Get-EspBootmgfwCopy {
$dl = $EspDriveLetter.ToUpper() $dl = $EspDriveLetter.ToUpper()
$driveRoot = "${dl}:" $driveRoot = "${dl}:"
# Pokus 1: ESP má přiřazené písmeno prohledat všechna písmena AZ # Pokus 1: ESP má přiřazené písmeno - prohledat všechna písmena A-Z
foreach ($d in [char[]]('A'..'Z')) { foreach ($d in [char[]]('A'..'Z')) {
$p = "${d}:\EFI\Microsoft\Boot\bootmgfw.efi" $p = "${d}:\EFI\Microsoft\Boot\bootmgfw.efi"
if (Test-Path -LiteralPath $p -ErrorAction SilentlyContinue) { if (Test-Path -LiteralPath $p -ErrorAction SilentlyContinue) {
@@ -701,7 +701,7 @@ function Get-EspBootmgfwCopy {
} }
if (-not (Test-Path -LiteralPath "${driveRoot}\" -ErrorAction SilentlyContinue)) { if (-not (Test-Path -LiteralPath "${driveRoot}\" -ErrorAction SilentlyContinue)) {
$out.Error = "Nepodařilo se připojit ESP na ${driveRoot} zkuste jiné písmeno parametrem -EspDriveLetter" $out.Error = "Nepodařilo se připojit ESP na ${driveRoot} - zkuste jiné písmeno parametrem -EspDriveLetter"
return $out return $out
} }
@@ -711,7 +711,7 @@ function Get-EspBootmgfwCopy {
$out.Error = "bootmgfw.efi nenalezen na ${driveRoot} (ESP připojen, ale soubor chybí)" $out.Error = "bootmgfw.efi nenalezen na ${driveRoot} (ESP připojen, ale soubor chybí)"
return $out return $out
} }
Add-LogLine ("ESP copy: ${src} ${dst}") Add-LogLine ("ESP copy: ${src} -> ${dst}")
Copy-Item -LiteralPath $src -Destination $dst -Force -ErrorAction Stop Copy-Item -LiteralPath $src -Destination $dst -Force -ErrorAction Stop
$out.Success = $true; $out.TempPath = $dst; $out.Source = $src $out.Success = $true; $out.TempPath = $dst; $out.Source = $src
} catch { } catch {
@@ -727,9 +727,9 @@ function Get-EspBootmgfwCopy {
function Get-BootManagerEvidence { function Get-BootManagerEvidence {
# Ověří CA verzi Boot Manageru (bootmgfw.efi) třemi vrstvami důkazů: # Ověří CA verzi Boot Manageru (bootmgfw.efi) třemi vrstvami důkazů:
# 1. certutil -dump čte embedded PKCS#7 blob přímo z PE, bez local cert store # 1. certutil -dump - čte embedded PKCS#7 blob přímo z PE, bez local cert store
# 2. X509Chain záloha pokud certutil chybí (závisí na local store) # 2. X509Chain - záloha pokud certutil chybí (závisí na local store)
# 3. Event ID 1799 "Boot Manager signed with Windows UEFI CA 2023 was installed" # 3. Event ID 1799 - "Boot Manager signed with Windows UEFI CA 2023 was installed"
# #
# Vrátí hashtable s: Checked, Is2023, Is2011, CAVersion, IssuerCA, # Vrátí hashtable s: Checked, Is2023, Is2011, CAVersion, IssuerCA,
# SignerSubject, Source, Method, Error # SignerSubject, Source, Method, Error
@@ -746,7 +746,7 @@ function Get-BootManagerEvidence {
Error = $null # chybová zpráva Error = $null # chybová zpráva
} }
# ── Krok 1: Zkopírovat bootmgfw.efi z aktivního ESP ─────────────────────── # -- Krok 1: Zkopírovat bootmgfw.efi z aktivního ESP -----------------------
$espCopy = Get-EspBootmgfwCopy $espCopy = Get-EspBootmgfwCopy
if (-not $espCopy.Success) { if (-not $espCopy.Success) {
$out.Error = $espCopy.Error $out.Error = $espCopy.Error
@@ -754,7 +754,7 @@ function Get-BootManagerEvidence {
$out.Source = $espCopy.Source $out.Source = $espCopy.Source
$file = $espCopy.TempPath $file = $espCopy.TempPath
try { try {
# ── Krok 2a: certutil -dump (primární) ──────────────────────────── # -- Krok 2a: certutil -dump (primární) ----------------------------
$certutil = Get-Command certutil.exe -ErrorAction SilentlyContinue $certutil = Get-Command certutil.exe -ErrorAction SilentlyContinue
if ($certutil) { if ($certutil) {
$raw = & certutil.exe -dump $file 2>&1 | Out-String $raw = & certutil.exe -dump $file 2>&1 | Out-String
@@ -773,18 +773,18 @@ function Get-BootManagerEvidence {
$out.Is2011 = $true; $out.CAVersion = '2011' $out.Is2011 = $true; $out.CAVersion = '2011'
$out.IssuerCA = 'Windows UEFI CA 2011' $out.IssuerCA = 'Windows UEFI CA 2011'
} }
# Pokud certutil nenašel žádnou CA out.CAVersion zůstane $null # Pokud certutil nenašel žádnou CA -> out.CAVersion zůstane $null
} }
# ── Krok 2b: Get-AuthenticodeSignature ──────────────────────────── # -- Krok 2b: Get-AuthenticodeSignature ----------------------------
# Vždy pro SignerSubject do logu. Když certutil nebyl dostupný, # Vždy - pro SignerSubject do logu. Když certutil nebyl dostupný,
# X509Chain slouží jako záloha pro Is2023/Is2011. # X509Chain slouží jako záloha pro Is2023/Is2011.
try { try {
$sig = Get-AuthenticodeSignature -FilePath $file -ErrorAction Stop $sig = Get-AuthenticodeSignature -FilePath $file -ErrorAction Stop
if ($sig.SignerCertificate) { if ($sig.SignerCertificate) {
$out.SignerSubject = $sig.SignerCertificate.Subject $out.SignerSubject = $sig.SignerCertificate.Subject
if (-not $certutil) { if (-not $certutil) {
# X509Chain fallback závisí na local cert store # X509Chain fallback - závisí na local cert store
$out.Checked = $true; $out.Method = 'x509chain' $out.Checked = $true; $out.Method = 'x509chain'
$chain = New-Object System.Security.Cryptography.X509Certificates.X509Chain $chain = New-Object System.Security.Cryptography.X509Certificates.X509Chain
$chain.ChainPolicy.RevocationMode = ` $chain.ChainPolicy.RevocationMode = `
@@ -810,7 +810,7 @@ function Get-BootManagerEvidence {
} }
} }
} }
} catch { } # AuthSig je doplňkový chybu ignorujeme } catch { } # AuthSig je doplňkový - chybu ignorujeme
} catch { } catch {
$out.Error = $_.Exception.Message $out.Error = $_.Exception.Message
@@ -819,7 +819,7 @@ function Get-BootManagerEvidence {
} }
} }
# ── Krok 3: Event ID 1799 terciální evidence ──────────────────────────── # -- Krok 3: Event ID 1799 - terciální evidence ----------------------------
# Zpráva "Boot Manager signed with Windows UEFI CA 2023 was installed successfully" # Zpráva "Boot Manager signed with Windows UEFI CA 2023 was installed successfully"
# Používá se jen jako podpora pokud certutil/chain ještě neprokázaly 2023. # Používá se jen jako podpora pokud certutil/chain ještě neprokázaly 2023.
if (-not $out.Is2023) { if (-not $out.Is2023) {
@@ -843,7 +843,7 @@ function Get-BootManagerEvidence {
#endregion #endregion
#region ── Detekce orchestrace ────────────────────────────────────────────── #region -- Detekce - orchestrace ----------------------------------------------
function Invoke-Detection { function Invoke-Detection {
$os = Get-CimInstance Win32_OperatingSystem -ErrorAction SilentlyContinue $os = Get-CimInstance Win32_OperatingSystem -ErrorAction SilentlyContinue
@@ -916,7 +916,7 @@ function Invoke-Detection {
#endregion #endregion
#region ── Zobrazení ────────────────────────────────────────────────────────── #region -- Zobrazení ----------------------------------------------------------
function Show-Prerequisites { function Show-Prerequisites {
param($Prereqs) param($Prereqs)
@@ -965,27 +965,27 @@ function Show-Certificates {
if ($item.Present) { if ($item.Present) {
Write-Host (" [{0}] {1,-44} {2}{3}" -f $SYM_DONE, $item.Label, $tag, $expiry) -ForegroundColor Green Write-Host (" [{0}] {1,-44} {2}{3}" -f $SYM_DONE, $item.Label, $tag, $expiry) -ForegroundColor Green
Add-LogLine ("[+] $($item.Label) přítomen$expiry") Add-LogLine ("[+] $($item.Label) - přítomen$expiry")
} elseif ($item.Required) { } elseif ($item.Required) {
Write-Host (" [{0}] {1,-44} {2}" -f $SYM_FAIL, $item.Label, $tag) -ForegroundColor Red Write-Host (" [{0}] {1,-44} {2}" -f $SYM_FAIL, $item.Label, $tag) -ForegroundColor Red
Add-LogLine ("[!] $($item.Label) CHYBÍ (povinný)") Add-LogLine ("[!] $($item.Label) - CHYBÍ (povinný)")
} else { } else {
Write-Host (" [{0}] {1,-44} {2}" -f $SYM_PENDING, $item.Label, $tag) -ForegroundColor DarkGray Write-Host (" [{0}] {1,-44} {2}" -f $SYM_PENDING, $item.Label, $tag) -ForegroundColor DarkGray
Add-LogLine ("[ ] $($item.Label) chybí (volitelný)") Add-LogLine ("[ ] $($item.Label) - chybí (volitelný)")
} }
} }
# ── Boot Manager verze CA z verifikace souboru ───────────────────────── # -- Boot Manager - verze CA z verifikace souboru -------------------------
Write-Host '' Write-Host ''
$bmLabel = 'Boot Manager (bootmgfw.efi)' $bmLabel = 'Boot Manager (bootmgfw.efi)'
$bmMethod = if ($BM -and $BM.Method) { " [$($BM.Method)]" } else { '' } $bmMethod = if ($BM -and $BM.Method) { " [$($BM.Method)]" } else { '' }
if ($BM -and $BM.Is2023) { if ($BM -and $BM.Is2023) {
$detail = "aktuální CA: $($BM.IssuerCA)$bmMethod" $detail = "aktuální - CA: $($BM.IssuerCA)$bmMethod"
Write-Host (" [{0}] {1,-44} {2}" -f $SYM_DONE, $bmLabel, $detail) -ForegroundColor Green Write-Host (" [{0}] {1,-44} {2}" -f $SYM_DONE, $bmLabel, $detail) -ForegroundColor Green
Add-LogLine "[+] Boot Manager: $detail" Add-LogLine "[+] Boot Manager: $detail"
} elseif ($BM -and $BM.Is2011) { } elseif ($BM -and $BM.Is2011) {
$detail = "ZASTARALÝ CA: $($BM.IssuerCA) nutná aktualizace$bmMethod" $detail = "ZASTARALÝ - CA: $($BM.IssuerCA) -> nutná aktualizace$bmMethod"
Write-Host (" [{0}] {1,-44} {2}" -f $SYM_FAIL, $bmLabel, $detail) -ForegroundColor Red Write-Host (" [{0}] {1,-44} {2}" -f $SYM_FAIL, $bmLabel, $detail) -ForegroundColor Red
Add-LogLine "[!] Boot Manager: $detail" Add-LogLine "[!] Boot Manager: $detail"
} elseif ($BM -and $BM.Checked) { } elseif ($BM -and $BM.Checked) {
@@ -993,7 +993,7 @@ function Show-Certificates {
$signerShort = if ($BM.SignerSubject) { $signerShort = if ($BM.SignerSubject) {
($BM.SignerSubject -replace '^CN=([^,]+).*','$1').Trim() ($BM.SignerSubject -replace '^CN=([^,]+).*','$1').Trim()
} else { '?' } } else { '?' }
$detail = "CA nezjištěna signer: $signerShort$bmMethod" $detail = "CA nezjištěna - signer: $signerShort$bmMethod"
Write-Host (" [{0}] {1,-44} {2}" -f $SYM_PENDING, $bmLabel, $detail) -ForegroundColor DarkGray Write-Host (" [{0}] {1,-44} {2}" -f $SYM_PENDING, $bmLabel, $detail) -ForegroundColor DarkGray
Add-LogLine "[ ] Boot Manager: $detail" Add-LogLine "[ ] Boot Manager: $detail"
} elseif ($BM) { } elseif ($BM) {
@@ -1036,7 +1036,7 @@ function Show-Registry {
function Show-Events { function Show-Events {
param($Evt) param($Evt)
Write-Head 'UDÁLOSTI (System log Secure Boot)' Write-Head 'UDÁLOSTI (System log - Secure Boot)'
if (-not $Evt.RelevantEvents -or $Evt.RelevantEvents.Count -eq 0) { if (-not $Evt.RelevantEvents -or $Evt.RelevantEvents.Count -eq 0) {
Write-Host ' (žádné relevantní události)' -ForegroundColor DarkGray Write-Host ' (žádné relevantní události)' -ForegroundColor DarkGray
Add-LogLine 'Události: žádné relevantní' Add-LogLine 'Události: žádné relevantní'
@@ -1054,7 +1054,7 @@ function Show-Events {
function Show-Status { function Show-Status {
# Zobrazí všechny 4 sekce + celkový stav. # Zobrazí všechny 4 sekce + celkový stav.
# STAV se nezobrazí, pokud některá hard prerekvizita selhala ta musí být # STAV se nezobrazí, pokud některá hard prerekvizita selhala - ta musí být
# vyřešena dřív, než má kategorie (computed z neúplných dat) jakýkoliv smysl. # vyřešena dřív, než má kategorie (computed z neúplných dat) jakýkoliv smysl.
param($R, $Prereqs) param($R, $Prereqs)
Show-Prerequisites -Prereqs $Prereqs Show-Prerequisites -Prereqs $Prereqs
@@ -1078,7 +1078,7 @@ function Show-TwoRestartBlock {
Write-Host ' *** Boot Manager 2023 je připraven na disku (staged) ***' -ForegroundColor Yellow Write-Host ' *** Boot Manager 2023 je připraven na disku (staged) ***' -ForegroundColor Yellow
Write-Host '' Write-Host ''
Write-Host ' Pro dokončení jsou potřeba 2 kroky:' -ForegroundColor White Write-Host ' Pro dokončení jsou potřeba 2 kroky:' -ForegroundColor White
Write-Host ' 1. RESTART aktivuje nový Boot Manager' -ForegroundColor Gray Write-Host ' 1. RESTART - aktivuje nový Boot Manager' -ForegroundColor Gray
Write-Host ' 2. Windows automaticky dokončí zbývající kroky po restartu' -ForegroundColor Gray Write-Host ' 2. Windows automaticky dokončí zbývající kroky po restartu' -ForegroundColor Gray
Write-Host '' Write-Host ''
if ($blActive -and $bl.UsesPcr) { if ($blActive -and $bl.UsesPcr) {
@@ -1087,12 +1087,12 @@ function Show-TwoRestartBlock {
} else { } else {
Write-Host ' BitLocker: Před restartem ověřte dostupnost recovery key.' -ForegroundColor Gray Write-Host ' BitLocker: Před restartem ověřte dostupnost recovery key.' -ForegroundColor Gray
} }
Add-LogLine 'AU=0x4100 zobrazen blok "2 kroky"' Add-LogLine 'AU=0x4100 - zobrazen blok "2 kroky"'
} }
#endregion #endregion
#region ── Souhlas + restart ────────────────────────────────────────────────── #region -- Souhlas + restart --------------------------------------------------
function Get-UserConsent { function Get-UserConsent {
param([string]$Question, [string]$Detail) param([string]$Question, [string]$Detail)
@@ -1101,7 +1101,7 @@ function Get-UserConsent {
return $true return $true
} }
if (-not [Environment]::UserInteractive) { if (-not [Environment]::UserInteractive) {
Write-Line ' Neinteraktivní relace bez -AssumeYes remediace se neprovede.' Yellow Write-Line ' Neinteraktivní relace bez -AssumeYes - remediace se neprovede.' Yellow
return $false return $false
} }
Write-Host '' Write-Host ''
@@ -1121,13 +1121,13 @@ function Invoke-RestartOffer {
if ($Auto) { if ($Auto) {
Write-Host '' Write-Host ''
Write-Host ' AutoRestart: restartuji server nyní...' -ForegroundColor Yellow Write-Host ' AutoRestart: restartuji server nyní...' -ForegroundColor Yellow
Add-LogLine 'Restart: -AutoRestart spouštím Restart-Computer' Add-LogLine 'Restart: -AutoRestart - spouštím Restart-Computer'
Flush-Log Flush-Log
Restart-Computer -Force Restart-Computer -Force
return return
} }
if (-not [Environment]::UserInteractive) { if (-not [Environment]::UserInteractive) {
Write-Line ' (Neinteraktivní relace restart proveďte ručně.)' DarkGray Write-Line ' (Neinteraktivní relace - restart proveďte ručně.)' DarkGray
return return
} }
Write-Host '' Write-Host ''
@@ -1136,11 +1136,11 @@ function Invoke-RestartOffer {
$yn = New-Object System.Management.Automation.Host.ChoiceDescription '&Ano', 'Restartovat server nyní' $yn = New-Object System.Management.Automation.Host.ChoiceDescription '&Ano', 'Restartovat server nyní'
$nn = New-Object System.Management.Automation.Host.ChoiceDescription '&Ne', 'Restartovat ručně' $nn = New-Object System.Management.Automation.Host.ChoiceDescription '&Ne', 'Restartovat ručně'
if ($Host.UI.PromptForChoice('', ' Restart:', [System.Management.Automation.Host.ChoiceDescription[]]@($yn,$nn), 1) -eq 0) { if ($Host.UI.PromptForChoice('', ' Restart:', [System.Management.Automation.Host.ChoiceDescription[]]@($yn,$nn), 1) -eq 0) {
Add-LogLine 'Restart: uživatel potvrdil spouštím Restart-Computer' Add-LogLine 'Restart: uživatel potvrdil - spouštím Restart-Computer'
Flush-Log Flush-Log
Restart-Computer -Force Restart-Computer -Force
} else { } else {
Write-Host ' Restart odložen restartujte server ručně co nejdříve.' -ForegroundColor Gray Write-Host ' Restart odložen - restartujte server ručně co nejdříve.' -ForegroundColor Gray
Add-LogLine 'Restart: uživatel odložil restart' Add-LogLine 'Restart: uživatel odložil restart'
} }
} catch { } catch {
@@ -1149,7 +1149,7 @@ function Invoke-RestartOffer {
Flush-Log Flush-Log
Restart-Computer -Force Restart-Computer -Force
} else { } else {
Write-Line ' Restart odložen restartujte server ručně co nejdříve.' Gray Write-Line ' Restart odložen - restartujte server ručně co nejdříve.' Gray
Add-LogLine 'Restart: uživatel odložil restart (fallback)' Add-LogLine 'Restart: uživatel odložil restart (fallback)'
} }
} }
@@ -1157,7 +1157,7 @@ function Invoke-RestartOffer {
#endregion #endregion
#region ── Předpoklady ──────────────────────────────────────────────────────── #region -- Předpoklady --------------------------------------------------------
function Get-Prerequisites { function Get-Prerequisites {
param($R, [bool]$IsAdmin) param($R, [bool]$IsAdmin)
@@ -1174,16 +1174,16 @@ function Get-Prerequisites {
# 2. Windows Server # 2. Windows Server
$isServer = $R.OSCaption -like '*Windows Server*' $isServer = $R.OSCaption -like '*Windows Server*'
$list += @{ $list += @{
Label = ("Windows Server (2016/2019/2022/2025) Zjištěno: {0} (build {1})" -f $R.OSCaption, $R.OSBuildFull) Label = ("Windows Server (2016/2019/2022/2025) - Zjištěno: {0} (build {1})" -f $R.OSCaption, $R.OSBuildFull)
Ok = $isServer; Hard = $true Ok = $isServer; Hard = $true
Note = if (-not $isServer) { 'Skript je určen pro Windows Server' } else { $null } Note = if (-not $isServer) { 'Skript je určen pro Windows Server' } else { $null }
} }
# 3. UEFI + Secure Boot zapnutý # 3. UEFI + Secure Boot zapnutý
$sbAllOk = $sb.IsUEFI -and $sb.IsSupported -and $sb.IsEnabled $sbAllOk = $sb.IsUEFI -and $sb.IsSupported -and $sb.IsEnabled
$sbNote = if (-not $sb.IsUEFI) { 'Legacy BIOS Secure Boot není k dispozici' } ` $sbNote = if (-not $sb.IsUEFI) { 'Legacy BIOS - Secure Boot není k dispozici' } `
elseif (-not $sb.IsSupported) { 'Secure Boot není podporováno' } ` elseif (-not $sb.IsSupported) { 'Secure Boot není podporováno' } `
elseif (-not $sb.IsEnabled) { 'Secure Boot je vypnutý zapněte v UEFI/BIOS' } ` elseif (-not $sb.IsEnabled) { 'Secure Boot je vypnutý - zapněte v UEFI/BIOS' } `
else { $null } else { $null }
$list += @{ $list += @{
Label = 'UEFI + Secure Boot zapnutý' Label = 'UEFI + Secure Boot zapnutý'
@@ -1192,15 +1192,15 @@ function Get-Prerequisites {
# 4. Stavové registry klíče přítomny (servicing klíč = záplata >= 10/2025) # 4. Stavové registry klíče přítomny (servicing klíč = záplata >= 10/2025)
$servNote = if (-not $R.Registry.ServicingKeyExists) { $servNote = if (-not $R.Registry.ServicingKeyExists) {
"build $($R.OSBuildFull) nainstalujte kumulativní update min. z 10/2025 (KB5066835)" "build $($R.OSBuildFull) - nainstalujte kumulativní update min. z 10/2025 (KB5066835)"
} else { $null } } else { $null }
$list += @{ $list += @{
Label = 'Úroveň záplat >= 10/2025 stavové registry klíče přítomny' Label = 'Úroveň záplat >= 10/2025 - stavové registry klíče přítomny'
Ok = $R.Registry.ServicingKeyExists; Hard = $true; Note = $servNote Ok = $R.Registry.ServicingKeyExists; Hard = $true; Note = $servNote
} }
# 5. Scheduled task Secure-Boot-Update # 5. Scheduled task Secure-Boot-Update
$taskNote = if (-not $R.TaskExists) { "Task '$TASK_NAME' chybí nainstalujte kumulativní update min. z 10/2025" } else { $null } $taskNote = if (-not $R.TaskExists) { "Task '$TASK_NAME' chybí - nainstalujte kumulativní update min. z 10/2025" } else { $null }
$list += @{ $list += @{
Label = ("Scheduled task '$TASK_NAME' k dispozici") Label = ("Scheduled task '$TASK_NAME' k dispozici")
Ok = [bool]$R.TaskExists; Hard = $true; Note = $taskNote Ok = [bool]$R.TaskExists; Hard = $true; Note = $taskNote
@@ -1209,9 +1209,9 @@ function Get-Prerequisites {
# 6. PowerShell SecureBoot cmdlety # 6. PowerShell SecureBoot cmdlety
$sbCmdletOk = $null -ne (Get-Command Get-SecureBootUEFI -ErrorAction SilentlyContinue) $sbCmdletOk = $null -ne (Get-Command Get-SecureBootUEFI -ErrorAction SilentlyContinue)
$list += @{ $list += @{
Label = 'PowerShell SecureBoot cmdlety Get-SecureBootUEFI dostupný' Label = 'PowerShell SecureBoot cmdlety - Get-SecureBootUEFI dostupný'
Ok = $sbCmdletOk; Hard = $true Ok = $sbCmdletOk; Hard = $true
Note = if (-not $sbCmdletOk) { 'Cmdlet nedostupný možná Legacy BIOS nebo chybí modul' } else { $null } Note = if (-not $sbCmdletOk) { 'Cmdlet nedostupný - možná Legacy BIOS nebo chybí modul' } else { $null }
} }
return $list return $list
@@ -1219,7 +1219,7 @@ function Get-Prerequisites {
#endregion #endregion
#region ── Remediace ────────────────────────────────────────────────────────── #region -- Remediace ----------------------------------------------------------
function Invoke-Remediation { function Invoke-Remediation {
$out = [ordered]@{ Status='Unknown'; Message=''; After=$null } $out = [ordered]@{ Status='Unknown'; Message=''; After=$null }
@@ -1227,8 +1227,8 @@ function Invoke-Remediation {
Add-LogLine 'REMEDIACE START' Add-LogLine 'REMEDIACE START'
# Krok 1/3: Nastavit registry # Krok 1/3: Nastavit registry
# AvailableUpdates = 0x5944 pouze jednou při prvním spuštění (null) nebo po dokončení (0). # AvailableUpdates = 0x5944 pouze jednou - při prvním spuštění (null) nebo po dokončení (0).
# Systém hodnotu sám spravuje: 0x59440x59040x51040x41040x41000x40000x0. # Systém hodnotu sám spravuje: 0x5944->0x5904->0x5104->0x4104->0x4100->0x4000->0x0.
$auCurrent = (Get-ItemProperty $REG_SECUREBOOT -ErrorAction SilentlyContinue).AvailableUpdates $auCurrent = (Get-ItemProperty $REG_SECUREBOOT -ErrorAction SilentlyContinue).AvailableUpdates
$auNeedsInit = ($null -eq $auCurrent) -or ([int]$auCurrent -eq 0) $auNeedsInit = ($null -eq $auCurrent) -or ([int]$auCurrent -eq 0)
$auInitLabel = if ($auNeedsInit) { '0x5944 (první inicializace)' } else { '0x{0:X} (zachováno, probíhá)' -f [int]$auCurrent } $auInitLabel = if ($auNeedsInit) { '0x5944 (první inicializace)' } else { '0x{0:X} (zachováno, probíhá)' -f [int]$auCurrent }
@@ -1244,7 +1244,7 @@ function Invoke-Remediation {
Set-ItemProperty -Path $REG_SECUREBOOT -Name 'AvailableUpdates' -Value $AVAILABLE_UPDATES_VALUE -Type DWord -Force -ErrorAction Stop Set-ItemProperty -Path $REG_SECUREBOOT -Name 'AvailableUpdates' -Value $AVAILABLE_UPDATES_VALUE -Type DWord -Force -ErrorAction Stop
$auCheck = (Get-ItemProperty $REG_SECUREBOOT -ErrorAction Stop).AvailableUpdates $auCheck = (Get-ItemProperty $REG_SECUREBOOT -ErrorAction Stop).AvailableUpdates
if ([int]$auCheck -ne $AVAILABLE_UPDATES_VALUE) { if ([int]$auCheck -ne $AVAILABLE_UPDATES_VALUE) {
throw ('Ověření selhalo AvailableUpdates=0x{0:X}, očekáváno 0x5944' -f [int]$auCheck) throw ('Ověření selhalo - AvailableUpdates=0x{0:X}, očekáváno 0x5944' -f [int]$auCheck)
} }
} }
@@ -1271,9 +1271,9 @@ function Invoke-Remediation {
Write-Host ' [2/3] Spouštím servicing task ' -NoNewline Write-Host ' [2/3] Spouštím servicing task ' -NoNewline
$task = Get-ScheduledTask -TaskPath $TASK_PATH -TaskName $TASK_NAME -ErrorAction SilentlyContinue $task = Get-ScheduledTask -TaskPath $TASK_PATH -TaskName $TASK_NAME -ErrorAction SilentlyContinue
if (-not $task) { if (-not $task) {
Write-Host ' task nenalezen.' -ForegroundColor Yellow Write-Host '- task nenalezen.' -ForegroundColor Yellow
Write-Line ' Registry nastavena; Windows zpracuje při servisním běhu.' DarkGray Write-Line ' Registry nastavena; Windows zpracuje při servisním běhu.' DarkGray
Add-LogLine 'Krok 2: task nenalezen přeskočeno' Add-LogLine 'Krok 2: task nenalezen - přeskočeno'
} else { } else {
$initAU = [int]((Get-ItemProperty $REG_SECUREBOOT -Name 'AvailableUpdates' -ErrorAction SilentlyContinue).AvailableUpdates) $initAU = [int]((Get-ItemProperty $REG_SECUREBOOT -Name 'AvailableUpdates' -ErrorAction SilentlyContinue).AvailableUpdates)
try { try {
@@ -1301,7 +1301,7 @@ function Invoke-Remediation {
} }
Add-LogLine ("Krok 2: elapsed={0}s, stav={1}, AvailableUpdates=0x{2:X}" -f $elapsed, $state, $finalAU) Add-LogLine ("Krok 2: elapsed={0}s, stav={1}, AvailableUpdates=0x{2:X}" -f $elapsed, $state, $finalAU)
} catch { } catch {
Write-Host ' nepodařilo se spustit.' -ForegroundColor Yellow Write-Host '- nepodařilo se spustit.' -ForegroundColor Yellow
Write-Line (" $($_.Exception.Message)") DarkGray Write-Line (" $($_.Exception.Message)") DarkGray
} }
} }
@@ -1321,7 +1321,7 @@ function Invoke-Remediation {
#endregion #endregion
#region ── Stav napříč restarty ─────────────────────────────────────────────── #region -- Stav napříč restarty -----------------------------------------------
function Save-ResumeState { function Save-ResumeState {
param($R, [int]$Cycle) param($R, [int]$Cycle)
@@ -1348,14 +1348,14 @@ function Clear-ResumeState {
#endregion #endregion
#region ── Main ─────────────────────────────────────────────────────────────── #region -- Main ---------------------------------------------------------------
$isWhatIf = [bool]$WhatIfPreference $isWhatIf = [bool]$WhatIfPreference
# Hlavička # Hlavička
Write-Host '' Write-Host ''
Write-Rule 'Cyan' Write-Rule 'Cyan'
Write-Host ' SECURE BOOT KONTROLA A REMEDIACE CERTIFIKÁTŮ' -ForegroundColor White Write-Host ' SECURE BOOT - KONTROLA A REMEDIACE CERTIFIKÁTŮ' -ForegroundColor White
Write-Host (" {0,-40} {1}" -f $env:COMPUTERNAME, (Get-Date -Format 'yyyy-MM-dd HH:mm')) -ForegroundColor DarkGray Write-Host (" {0,-40} {1}" -f $env:COMPUTERNAME, (Get-Date -Format 'yyyy-MM-dd HH:mm')) -ForegroundColor DarkGray
Write-Rule 'Cyan' Write-Rule 'Cyan'
@@ -1380,21 +1380,23 @@ $prereqs = Get-Prerequisites -R $result -IsAdmin $isAdmin
# Zobrazení stavu (4 sekce: předpoklady, certifikáty, registry, události) # Zobrazení stavu (4 sekce: předpoklady, certifikáty, registry, události)
Show-Status -R $result -Prereqs $prereqs Show-Status -R $result -Prereqs $prereqs
# ── VMware: upozornění na manuální import KEK certifikátu ──────────────────── # -- VMware: upozornění na manuální import KEK certifikátu --------------------
# Pokud bylo VM vytvořeno na ESXi starším než 9.x, nelze KEK nasadit automaticky. # Pokud bylo VM vytvořeno na ESXi starším než 9.x, nelze KEK nasadit automaticky.
if ($result.EnvironmentType -eq 'VMware VM' -and $result.SecureBoot.IsEnabled) { if ($result.EnvironmentType -eq 'VMware VM' -and $result.SecureBoot.IsEnabled) {
Write-Host '' Write-Host ''
Write-Host ' ┌─ UPOZORNĚNÍ VMware VM ───────────────────────────────────────────────────┐' -ForegroundColor Yellow Write-Host ' +- UPOZORNĚNÍ - VMware VM -------------------------------------------------+' -ForegroundColor Yellow
Write-Host ' Pokud bylo toto VM vytvořeno na VMware ESXi starším než verze 9.x, ' -ForegroundColor Yellow Write-Host ' | Pokud bylo toto VM vytvořeno na VMware ESXi starším než verze 9.x, |' -ForegroundColor Yellow
Write-Host ' certifikát KEK nelze nasadit automaticky a vyžaduje ruční import do ' -ForegroundColor Yellow Write-Host ' | certifikát KEK nelze nasadit automaticky a vyžaduje ruční import do |' -ForegroundColor Yellow
Write-Host ' UEFI firmware VM prostřednictvím správce ESXi hostitele. ' -ForegroundColor Yellow Write-Host ' | UEFI firmware VM prostřednictvím správce ESXi hostitele. |' -ForegroundColor Yellow
Write-Host ' ' -ForegroundColor Yellow Write-Host ' | |' -ForegroundColor Yellow
Write-Host ' │ Postup: https://totalservice.atlassian.net/browse/KB-543 │' -ForegroundColor Cyan Write-Host ' | ' -ForegroundColor Yellow -NoNewline
Write-Host ' └──────────────────────────────────────────────────────────────────────────┘' -ForegroundColor Yellow Write-Host 'Postup: https://totalservice.atlassian.net/browse/KB-543 ' -ForegroundColor Cyan -NoNewline
Add-LogLine 'Upozornění: VMware VM — na ESXi < 9.x je nutný ruční import KEK (KB-543)' Write-Host '|' -ForegroundColor Yellow
Write-Host ' +--------------------------------------------------------------------------+' -ForegroundColor Yellow
Add-LogLine 'Upozornění: VMware VM - na ESXi < 9.x je nutný ruční import KEK (KB-543)'
} }
# ── CheckOnly: konec bez akce ───────────────────────────────────────────────── # -- CheckOnly: konec bez akce -------------------------------------------------
if ($CheckOnly) { if ($CheckOnly) {
Write-Host ''; Write-Rule Write-Host ''; Write-Rule
$exitCode = switch -Wildcard ($result.Category) { 'OK*' { 0 } 'UPDATE_*' { 1 } default { 2 } } $exitCode = switch -Wildcard ($result.Category) { 'OK*' { 0 } 'UPDATE_*' { 1 } default { 2 } }
@@ -1405,20 +1407,20 @@ if ($CheckOnly) {
exit $exitCode exit $exitCode
} }
# ── Ověřit hard prerekvizity ───────────────────────────────────────────────── # -- Ověřit hard prerekvizity -------------------------------------------------
$hardFails = @($prereqs | Where-Object { $_.Hard -and -not $_.Ok }) $hardFails = @($prereqs | Where-Object { $_.Hard -and -not $_.Ok })
if ($hardFails.Count) { if ($hardFails.Count) {
Write-Host '' Write-Host ''
Write-Host ' Nelze pokračovat vyřešte výše označené problémy a spusťte skript znovu.' -ForegroundColor Red Write-Host ' Nelze pokračovat - vyřešte výše označené problémy a spusťte skript znovu.' -ForegroundColor Red
Flush-Log Flush-Log
exit 2 exit 2
} }
# ── Blokující kategorie ─────────────────────────────────────────────────────── # -- Blokující kategorie -------------------------------------------------------
$blockedMap = @{ $blockedMap = @{
'NO_SECUREBOOT' = 'Secure Boot není podporováno (Legacy BIOS). Dokumentujte server jako výjimku.' 'NO_SECUREBOOT' = 'Secure Boot není podporováno (Legacy BIOS). Dokumentujte server jako výjimku.'
'NO_SECUREBOOT_VM' = 'Secure Boot není podporováno (VM bez UEFI nebo vTPM). Dokumentujte jako výjimku.' 'NO_SECUREBOOT_VM' = 'Secure Boot není podporováno (VM bez UEFI nebo vTPM). Dokumentujte jako výjimku.'
'SECUREBOOT_DISABLED' = 'Secure Boot je vypnutý. Zapnutí je mimo rozsah tohoto skriptu nastavte v UEFI/BIOS.' 'SECUREBOOT_DISABLED' = 'Secure Boot je vypnutý. Zapnutí je mimo rozsah tohoto skriptu - nastavte v UEFI/BIOS.'
'SETUP_MODE' = 'Secure Boot je v Setup Mode. V UEFI/BIOS obnovte Secure Boot klíče (enroll PK) a spusťte skript znovu.' 'SETUP_MODE' = 'Secure Boot je v Setup Mode. V UEFI/BIOS obnovte Secure Boot klíče (enroll PK) a spusťte skript znovu.'
'NOT_SUPPORTED' = 'Zařízení nepodporuje automatickou aktualizaci (ConfidenceLevel: Not Supported). Kontaktujte výrobce firmware.' 'NOT_SUPPORTED' = 'Zařízení nepodporuje automatickou aktualizaci (ConfidenceLevel: Not Supported). Kontaktujte výrobce firmware.'
'BUILD_OUTDATED' = 'Tento build Windows neumí nasadit 2023 certifikáty. Nainstalujte Windows Update (min. 10/2025, KB5066835) a restartujte.' 'BUILD_OUTDATED' = 'Tento build Windows neumí nasadit 2023 certifikáty. Nainstalujte Windows Update (min. 10/2025, KB5066835) a restartujte.'
@@ -1432,14 +1434,14 @@ if ($blockedMap.ContainsKey($result.Category)) {
} }
if ($result.Category -eq 'FIRMWARE_UPDATE_NEEDED') { if ($result.Category -eq 'FIRMWARE_UPDATE_NEEDED') {
Write-Host '' Write-Host ''
Write-Host (" Aktualizace pozastavena firmware serveru {0} ji nepodporuje." -f $result.Hardware.Manufacturer) -ForegroundColor Yellow Write-Host (" Aktualizace pozastavena - firmware serveru {0} ji nepodporuje." -f $result.Hardware.Manufacturer) -ForegroundColor Yellow
Write-Host ' Aktualizujte firmware u výrobce a poté spusťte skript znovu.' -ForegroundColor Gray Write-Host ' Aktualizujte firmware u výrobce a poté spusťte skript znovu.' -ForegroundColor Gray
Save-ResumeState -R $result -Cycle $(if ($prev) { [int]$prev.Cycle } else { 0 }) Save-ResumeState -R $result -Cycle $(if ($prev) { [int]$prev.Cycle } else { 0 })
Flush-Log Flush-Log
exit 2 exit 2
} }
# ── UPDATE_FAILED upozornit, ale přesto nabídnout opakování ──────────────── # -- UPDATE_FAILED - upozornit, ale přesto nabídnout opakování ----------------
if ($result.Category -eq 'UPDATE_FAILED') { if ($result.Category -eq 'UPDATE_FAILED') {
Write-Host '' Write-Host ''
$errEvtId = $result.Registry.UEFICA2023ErrorEvent $errEvtId = $result.Registry.UEFICA2023ErrorEvent
@@ -1454,8 +1456,8 @@ if ($result.Category -eq 'UPDATE_FAILED') {
$cycle = if ($prev) { [int]$prev.Cycle } else { 0 } $cycle = if ($prev) { [int]$prev.Cycle } else { 0 }
# ── Předběžná kontrola: povinné 2023 certy přítomny? ───────────────────────── # -- Předběžná kontrola: povinné 2023 certy přítomny? -------------------------
# Pokud ano a není -Force ukončit bez remediace. # Pokud ano a není -Force -> ukončit bez remediace.
$certsReady = $result.Certificates.KEK.Has2023 -and $result.Certificates.DB.Has2023WindowsUEFI $certsReady = $result.Certificates.KEK.Has2023 -and $result.Certificates.DB.Has2023WindowsUEFI
if ($certsReady -and ($result.Category -like 'OK*') -and -not $Force) { if ($certsReady -and ($result.Category -like 'OK*') -and -not $Force) {
Write-Host '' Write-Host ''
@@ -1475,14 +1477,14 @@ if ($certsReady -and $Force) {
Write-Host ' ! Certifikáty jsou přítomny, ale -Force umožňuje spustit remediaci znovu.' -ForegroundColor Yellow Write-Host ' ! Certifikáty jsou přítomny, ale -Force umožňuje spustit remediaci znovu.' -ForegroundColor Yellow
} }
# ── UPDATE_BOOTMANAGER: certy OK, ale bootloader nepoužívá 2023 CA ─────────── # -- UPDATE_BOOTMANAGER: certy OK, ale bootloader nepoužívá 2023 CA -----------
if ($result.Category -eq 'UPDATE_BOOTMANAGER') { if ($result.Category -eq 'UPDATE_BOOTMANAGER') {
Write-Host '' Write-Host ''
Write-Host ' Certifikáty jsou v UEFI databázích, ale Boot Manager dosud nepoužívá 2023 CA.' -ForegroundColor Yellow Write-Host ' Certifikáty jsou v UEFI databázích, ale Boot Manager dosud nepoužívá 2023 CA.' -ForegroundColor Yellow
Write-Host ' Remediace spustí task, který Boot Manager 2023 připraví poté bude nutný restart.' -ForegroundColor Gray Write-Host ' Remediace spustí task, který Boot Manager 2023 připraví - poté bude nutný restart.' -ForegroundColor Gray
} }
# ── AU=0x4100: Boot Manager staged, stačí RESTART ──────────────────────────── # -- AU=0x4100: Boot Manager staged, stačí RESTART ----------------------------
$auNow = [int]$result.Registry.AvailableUpdates $auNow = [int]$result.Registry.AvailableUpdates
if ($auNow -eq 0x4100) { if ($auNow -eq 0x4100) {
Show-TwoRestartBlock -R $result Show-TwoRestartBlock -R $result
@@ -1497,7 +1499,7 @@ if ($auNow -eq 0x4100) {
exit 1 exit 1
} }
# ── Zkontrolovat administrátorská práva před remediací ──────────────────────── # -- Zkontrolovat administrátorská práva před remediací ------------------------
if (-not $isAdmin) { if (-not $isAdmin) {
Write-Host '' Write-Host ''
Write-Host ' Remediaci nelze provést bez práv Administrator.' -ForegroundColor Yellow Write-Host ' Remediaci nelze provést bez práv Administrator.' -ForegroundColor Yellow
@@ -1506,11 +1508,11 @@ if (-not $isAdmin) {
exit 2 exit 2
} }
# ── WhatIf: zobrazit co by se stalo ───────────────────────────────────────── # -- WhatIf: zobrazit co by se stalo -----------------------------------------
$remediation = $null $remediation = $null
if ($isWhatIf) { if ($isWhatIf) {
Write-Host '' Write-Host ''
Write-Host ' -WhatIf co by remediace udělala (bez změn):' -ForegroundColor Cyan Write-Host ' -WhatIf - co by remediace udělala (bez změn):' -ForegroundColor Cyan
Write-Host ' - Nastaví MicrosoftUpdateManagedOptIn=1, HighConfidenceOptOut=0' -ForegroundColor Gray Write-Host ' - Nastaví MicrosoftUpdateManagedOptIn=1, HighConfidenceOptOut=0' -ForegroundColor Gray
$auWhatIfLabel = if ($auNow -eq 0) { '0x5944 (první inicializace)' } else { '0x{0:X} (zachováno)' -f $auNow } $auWhatIfLabel = if ($auNow -eq 0) { '0x5944 (první inicializace)' } else { '0x{0:X} (zachováno)' -f $auNow }
Write-Host (" - AvailableUpdates: {0}" -f $auWhatIfLabel) -ForegroundColor Gray Write-Host (" - AvailableUpdates: {0}" -f $auWhatIfLabel) -ForegroundColor Gray
@@ -1518,34 +1520,34 @@ if ($isWhatIf) {
$autoRestartNote = if ($AutoRestart) { 'ANO (-AutoRestart)' } else { 'NE (nabídne dotaz)' } $autoRestartNote = if ($AutoRestart) { 'ANO (-AutoRestart)' } else { 'NE (nabídne dotaz)' }
Write-Host (" - Restart serveru: {0}" -f $autoRestartNote) -ForegroundColor Gray Write-Host (" - Restart serveru: {0}" -f $autoRestartNote) -ForegroundColor Gray
} else { } else {
# ── Upozornění na BitLocker před souhlasem s remediací ─────────────────── # -- Upozornění na BitLocker před souhlasem s remediací -------------------
# Zobrazuje se vždy, když je BitLocker aktivní bez ohledu na typ protektoru. # Zobrazuje se vždy, když je BitLocker aktivní - bez ohledu na typ protektoru.
$blPre = $result.BitLocker $blPre = $result.BitLocker
$blPreActv = $blPre -and ($blPre.Status -eq 'On' -or $blPre.Status -eq '1' -or $blPre.Status -eq '2') $blPreActv = $blPre -and ($blPre.Status -eq 'On' -or $blPre.Status -eq '1' -or $blPre.Status -eq '2')
if ($blPreActv) { if ($blPreActv) {
Write-Host '' Write-Host ''
if ($blPre.UsesPcr) { if ($blPre.UsesPcr) {
Write-Host ' ┌─ DOPORUČENÍ PŘED REMEDIACÍ BitLocker ───────────────────────────────────┐' -ForegroundColor Yellow Write-Host ' +- DOPORUČENÍ PŘED REMEDIACÍ - BitLocker -----------------------------------+' -ForegroundColor Yellow
Write-Host ' Systémový svazek je chráněn nástrojem BitLocker s vazbou na TPM/PCR7. ' -ForegroundColor Yellow Write-Host ' | Systémový svazek je chráněn nástrojem BitLocker s vazbou na TPM/PCR7. |' -ForegroundColor Yellow
Write-Host ' Aktualizace Boot Manageru může při příštím restartu vyvolat recovery ' -ForegroundColor Yellow Write-Host ' | Aktualizace Boot Manageru může při příštím restartu vyvolat recovery |' -ForegroundColor Yellow
Write-Host ' prompt a znemožnit automatické spuštění serveru. ' -ForegroundColor Yellow Write-Host ' | prompt a znemožnit automatické spuštění serveru. |' -ForegroundColor Yellow
Write-Host ' ' -ForegroundColor Yellow Write-Host ' | |' -ForegroundColor Yellow
Write-Host ' Před zahájením remediace ověřte dostupnost recovery klíče: ' -ForegroundColor Yellow Write-Host ' | Před zahájením remediace ověřte dostupnost recovery klíče: |' -ForegroundColor Yellow
Write-Host ' Active Directory (AD DS), Azure AD nebo bezpečný trezor klíčů. ' -ForegroundColor Yellow Write-Host ' | Active Directory (AD DS), Azure AD nebo bezpečný trezor klíčů. |' -ForegroundColor Yellow
Write-Host ' └──────────────────────────────────────────────────────────────────────────┘' -ForegroundColor Yellow Write-Host ' +--------------------------------------------------------------------------+' -ForegroundColor Yellow
} else { } else {
Write-Host ' ┌─ DOPORUČENÍ PŘED REMEDIACÍ BitLocker ───────────────────────────────────┐' -ForegroundColor Yellow Write-Host ' +- DOPORUČENÍ PŘED REMEDIACÍ - BitLocker -----------------------------------+' -ForegroundColor Yellow
Write-Host ' Systémový svazek je chráněn nástrojem BitLocker. ' -ForegroundColor Yellow Write-Host ' | Systémový svazek je chráněn nástrojem BitLocker. |' -ForegroundColor Yellow
Write-Host ' Před zahájením remediace ověřte dostupnost recovery klíče (AD, trezor). ' -ForegroundColor Yellow Write-Host ' | Před zahájením remediace ověřte dostupnost recovery klíče (AD, trezor). |' -ForegroundColor Yellow
Write-Host ' └──────────────────────────────────────────────────────────────────────────┘' -ForegroundColor Yellow Write-Host ' +--------------------------------------------------------------------------+' -ForegroundColor Yellow
} }
Add-LogLine ("Upozornění: BitLocker aktivní před remediací (Protectors: {0}, PCR: {1})" -f $blPre.Protectors, $blPre.UsesPcr) Add-LogLine ("Upozornění: BitLocker aktivní před remediací (Protectors: {0}, PCR: {1})" -f $blPre.Protectors, $blPre.UsesPcr)
} }
# ── Nabídnout remediaci ────────────────────────────────────────────────── # -- Nabídnout remediaci --------------------------------------------------
$question = switch ($result.Category) { $question = switch ($result.Category) {
'UPDATE_NEEDED' { 'Server potřebuje aktualizaci Secure Boot certifikátů. Chcete zahájit proces?' } 'UPDATE_NEEDED' { 'Server potřebuje aktualizaci Secure Boot certifikátů. Chcete zahájit proces?' }
'UPDATE_PARTIAL' { 'Aktualizace probíhá část certifikátů je nasazena. Spustit task pro posun procesu?' } 'UPDATE_PARTIAL' { 'Aktualizace probíhá - část certifikátů je nasazena. Spustit task pro posun procesu?' }
'UPDATE_PENDING_RESTART' { 'Certifikáty jsou v procesu. Spustit servicing task?' } 'UPDATE_PENDING_RESTART' { 'Certifikáty jsou v procesu. Spustit servicing task?' }
'UPDATE_FAILED' { 'Předchozí pokus selhal. Chcete zkusit remediaci znovu?' } 'UPDATE_FAILED' { 'Předchozí pokus selhal. Chcete zkusit remediaci znovu?' }
default { 'Chcete spustit remediaci Secure Boot certifikátů?' } default { 'Chcete spustit remediaci Secure Boot certifikátů?' }
@@ -1559,11 +1561,11 @@ if ($isWhatIf) {
Flush-Log Flush-Log
} else { } else {
Write-Host '' Write-Host ''
Write-Host ' Remediace neprovedena spusťte skript znovu, až budete připraveni.' -ForegroundColor Yellow Write-Host ' Remediace neprovedena - spusťte skript znovu, až budete připraveni.' -ForegroundColor Yellow
} }
} }
# ── Závěr ───────────────────────────────────────────────────────────────────── # -- Závěr ---------------------------------------------------------------------
Write-Host ''; Write-Rule 'Cyan' Write-Host ''; Write-Rule 'Cyan'
if ($remediation -and $remediation.Status -eq 'Applied') { if ($remediation -and $remediation.Status -eq 'Applied') {
@@ -1577,7 +1579,7 @@ if ($remediation -and $remediation.Status -eq 'Applied') {
Write-Host '' Write-Host ''
if ($afterDone) { if ($afterDone) {
Write-Host (" [{0}] HOTOVO Secure Boot certifikáty 2023 úspěšně nasazeny!" -f $SYM_DONE) -ForegroundColor Green Write-Host (" [{0}] HOTOVO - Secure Boot certifikáty 2023 úspěšně nasazeny!" -f $SYM_DONE) -ForegroundColor Green
Write-Host ' Podmínky splněny: certifikáty v DB/KEK, UEFICA2023Status=Updated, AvailableUpdates=0x0/0x4000.' -ForegroundColor Green Write-Host ' Podmínky splněny: certifikáty v DB/KEK, UEFICA2023Status=Updated, AvailableUpdates=0x0/0x4000.' -ForegroundColor Green
Clear-ResumeState Clear-ResumeState
} elseif ($afterAU -eq 0x4100) { } elseif ($afterAU -eq 0x4100) {
@@ -1585,20 +1587,20 @@ if ($remediation -and $remediation.Status -eq 'Applied') {
Invoke-RestartOffer -Auto:$AutoRestart.IsPresent Invoke-RestartOffer -Auto:$AutoRestart.IsPresent
Save-ResumeState -R $after -Cycle $cycle Save-ResumeState -R $after -Cycle $cycle
} else { } else {
Write-Host ' Remediace proběhla proces vyžaduje restart pro dokončení.' -ForegroundColor Yellow Write-Host ' Remediace proběhla - proces vyžaduje restart pro dokončení.' -ForegroundColor Yellow
Write-Host '' Write-Host ''
Write-Host ' Další kroky:' -ForegroundColor White Write-Host ' Další kroky:' -ForegroundColor White
Write-Host ' 1. Restartujte server.' -ForegroundColor Gray Write-Host ' 1. Restartujte server.' -ForegroundColor Gray
Write-Host ' 2. Spusťte skript znovu opakujte, dokud nebude HOTOVO.' -ForegroundColor Gray Write-Host ' 2. Spusťte skript znovu - opakujte, dokud nebude HOTOVO.' -ForegroundColor Gray
Write-Host (" 3. Cíl: AvailableUpdates=0x0, UEFICA2023Status=Updated, certifikáty v KEK+DB.") -ForegroundColor DarkGray Write-Host (" 3. Cíl: AvailableUpdates=0x0, UEFICA2023Status=Updated, certifikáty v KEK+DB.") -ForegroundColor DarkGray
Save-ResumeState -R $after -Cycle $cycle Save-ResumeState -R $after -Cycle $cycle
} }
} elseif ($remediation -and $remediation.Status -eq 'Error') { } elseif ($remediation -and $remediation.Status -eq 'Error') {
Write-Host (" CHYBA REMEDIACE {0}" -f $remediation.Message) -ForegroundColor Red Write-Host (" CHYBA REMEDIACE - {0}" -f $remediation.Message) -ForegroundColor Red
Save-ResumeState -R $result -Cycle $cycle Save-ResumeState -R $result -Cycle $cycle
} else { } else {
# Bez remediace (odmítnutí / WhatIf / blocked) # Bez remediace (odmítnutí / WhatIf / blocked)
Write-Host ' KONEC bez změn na serveru.' -ForegroundColor Cyan Write-Host ' KONEC - bez změn na serveru.' -ForegroundColor Cyan
$finalR = $result $finalR = $result
if ($finalR.Category -like 'OK*') { Clear-ResumeState } else { Save-ResumeState -R $finalR -Cycle $cycle } if ($finalR.Category -like 'OK*') { Clear-ResumeState } else { Save-ResumeState -R $finalR -Cycle $cycle }
} }