Add Invoke-SecureBootRemediation.ps1
This commit is contained in:
+320
-138
@@ -15,12 +15,12 @@
|
|||||||
- ConfidenceLevel (REG_SZ): High Confidence / Temporarily Paused / Not Supported / ...
|
- ConfidenceLevel (REG_SZ): High Confidence / Temporarily Paused / Not Supported / ...
|
||||||
- AvailableUpdates: 0x5944 -> 0x5904 -> 0x5104 -> 0x4104 -> 0x4100 -> 0x4000 -> 0x0
|
- AvailableUpdates: 0x5944 -> 0x5904 -> 0x5104 -> 0x4104 -> 0x4100 -> 0x4000 -> 0x0
|
||||||
- Cílový stav: AvailableUpdates=0x0, UEFICA2023Status=Updated, WindowsUEFICA2023Capable=2
|
- Cílový stav: AvailableUpdates=0x0, UEFICA2023Status=Updated, WindowsUEFICA2023Capable=2
|
||||||
- Events (TPM-WMI): 1808=hotovo, 1799=boot mgr 2023, 1795=chyba firmwaru,
|
- Events (TPM-WMI): 1795=chyba firmwaru, 1796=selhání zápisu UEFI var,
|
||||||
1802=pozastaveno, 1803=odloženo (monitor), 1801=zatím neaplikováno (monitor)
|
1802=pozastaveno (firmware), 1803=odloženo (monitor), 1801=čeká (monitor)
|
||||||
|
|
||||||
Skript ZÁMĚRNĚ NERESTARTUJE server. Sleduje stav i napříč restarty (state.json) a umí
|
Skript nabídne restart (interaktivní dotaz) jen při AvailableUpdates=0x4100 (Boot Manager
|
||||||
volitelně po dalším restartu spustit kontrolu automaticky (-RegisterResume) — stále BEZ
|
staged). Ve všech ostatních stavech task spustí a posune hodnotu. Sleduje stav napříč
|
||||||
auto-restartu.
|
restarty (state.json). Volitelně registruje RunOnce pro auto-check po restartu (-RegisterResume).
|
||||||
|
|
||||||
.PARAMETER CheckOnly
|
.PARAMETER CheckOnly
|
||||||
Jen detekce + checklist + exit kód (0=hotovo, 1=nutná akce, 2=blokováno). Bez změn/dotazu.
|
Jen detekce + checklist + exit kód (0=hotovo, 1=nutná akce, 2=blokováno). Bez změn/dotazu.
|
||||||
@@ -35,8 +35,8 @@
|
|||||||
Nastaví registry, ale nespustí servicing task (spustí se sám cca á 12 h).
|
Nastaví registry, ale nespustí servicing task (spustí se sám cca á 12 h).
|
||||||
|
|
||||||
.PARAMETER SkipBootManagerFileCheck
|
.PARAMETER SkipBootManagerFileCheck
|
||||||
Neověřuje bootmgfw.efi na ESP přes mount+certutil. Dokončení se i tak pozná z
|
Neověřuje bootmgfw.efi na ESP přes mount+certutil. Dokončení se pozná z
|
||||||
WindowsUEFICA2023Capable=2 / Event 1808 / 1799.
|
WindowsUEFICA2023Capable=2 a UEFICA2023Status=Updated.
|
||||||
|
|
||||||
.PARAMETER RegisterResume
|
.PARAMETER RegisterResume
|
||||||
Po remediaci, kde zbývá restart, zaregistruje RunOnce, který po PŘÍŠTÍM (ručním) restartu
|
Po remediaci, kde zbývá restart, zaregistruje RunOnce, který po PŘÍŠTÍM (ručním) restartu
|
||||||
@@ -123,7 +123,6 @@ $SYM_PENDING = ' '
|
|||||||
|
|
||||||
#region ── Log / barevný výstup ───────────────────────────────────────────────
|
#region ── Log / barevný výstup ───────────────────────────────────────────────
|
||||||
|
|
||||||
$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.
|
# 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áří
|
# 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é.
|
# poškozené (null) soubory. Řádky se bufferují a zapíšou jednorázově (Flush-Log), což je odolné.
|
||||||
@@ -243,25 +242,44 @@ function Get-BitLockerInfo {
|
|||||||
#region ── Detekce — certifikáty / registry / events / boot manager ───────────
|
#region ── Detekce — certifikáty / registry / events / boot manager ───────────
|
||||||
|
|
||||||
function Parse-EFISignatureList {
|
function Parse-EFISignatureList {
|
||||||
|
# Parsuje EFI_SIGNATURE_LIST a vrátí pole X509Certificate2 (pouze X.509 položky).
|
||||||
|
# Formát: opakující se hlavičky SignatureList, v každé seznam SignatureEntry s 16B GUID prefixem.
|
||||||
param([byte[]]$Bytes)
|
param([byte[]]$Bytes)
|
||||||
$certs=@(); if (-not $Bytes -or $Bytes.Length -lt 28) { return $certs }
|
$certs = @()
|
||||||
$G=[byte[]](0xa1,0x59,0xc0,0xa5,0xe4,0x94,0xa7,0x4a,0x87,0xb5,0xab,0x15,0x5c,0x2b,0xf0,0x72)
|
if (-not $Bytes -or $Bytes.Length -lt 28) { return $certs }
|
||||||
$o=0
|
|
||||||
while ($o + 28 -le $Bytes.Length) {
|
# GUID pro EFI_CERT_X509_GUID: {a5c059a1-94e4-4aa7-87b5-ab155c2bf072} (little-endian bytes)
|
||||||
$gid=$Bytes[$o..($o+15)]; $listSize=[BitConverter]::ToUInt32($Bytes,$o+16); $hdr=[BitConverter]::ToUInt32($Bytes,$o+20); $sz=[BitConverter]::ToUInt32($Bytes,$o+24)
|
$x509Guid = [byte[]](0xa1,0x59,0xc0,0xa5,0xe4,0x94,0xa7,0x4a,0x87,0xb5,0xab,0x15,0x5c,0x2b,0xf0,0x72)
|
||||||
if ($listSize -lt 28 -or $listSize -gt ($Bytes.Length-$o)) { break }
|
|
||||||
$x=$true; for ($i=0;$i -lt 16;$i++){ if ($gid[$i] -ne $G[$i]){ $x=$false; break } }
|
$pos = 0
|
||||||
if ($x -and $sz -gt 16) {
|
while ($pos + 28 -le $Bytes.Length) {
|
||||||
$so=$o+28+$hdr; $end=$o+$listSize
|
$sigTypeGuid = $Bytes[$pos..($pos+15)]
|
||||||
while ($so+$sz -le $end) {
|
$listSize = [BitConverter]::ToUInt32($Bytes, $pos+16)
|
||||||
$co=$so+16; $cs=[int]$sz-16
|
$headerSize = [BitConverter]::ToUInt32($Bytes, $pos+20)
|
||||||
if ($co+$cs -le $Bytes.Length -and $cs -gt 0) {
|
$sigSize = [BitConverter]::ToUInt32($Bytes, $pos+24)
|
||||||
try { $certs += New-Object System.Security.Cryptography.X509Certificates.X509Certificate2(, [byte[]]($Bytes[$co..($co+$cs-1)])) } catch { }
|
|
||||||
|
if ($listSize -lt 28 -or $listSize -gt ($Bytes.Length - $pos)) { break }
|
||||||
|
|
||||||
|
# Zpracovat pouze X.509 záznamy
|
||||||
|
$isX509 = $true
|
||||||
|
for ($i = 0; $i -lt 16; $i++) { if ($sigTypeGuid[$i] -ne $x509Guid[$i]) { $isX509 = $false; break } }
|
||||||
|
|
||||||
|
if ($isX509 -and $sigSize -gt 16) {
|
||||||
|
$entryPos = $pos + 28 + $headerSize
|
||||||
|
$listEnd = $pos + $listSize
|
||||||
|
while ($entryPos + $sigSize -le $listEnd) {
|
||||||
|
$certOffset = $entryPos + 16 # přeskočit 16B SignatureOwner GUID
|
||||||
|
$certSize = [int]$sigSize - 16
|
||||||
|
if ($certOffset + $certSize -le $Bytes.Length -and $certSize -gt 0) {
|
||||||
|
try {
|
||||||
|
$certBytes = [byte[]]($Bytes[$certOffset..($certOffset + $certSize - 1)])
|
||||||
|
$certs += New-Object System.Security.Cryptography.X509Certificates.X509Certificate2(, $certBytes)
|
||||||
|
} catch { }
|
||||||
}
|
}
|
||||||
$so += $sz
|
$entryPos += $sigSize
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$o += $listSize
|
$pos += $listSize
|
||||||
}
|
}
|
||||||
return $certs
|
return $certs
|
||||||
}
|
}
|
||||||
@@ -281,20 +299,25 @@ function Get-CertificateStatus {
|
|||||||
DbxRevokesPCA2011=$false; AnyExpiring2011=$false
|
DbxRevokesPCA2011=$false; AnyExpiring2011=$false
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
foreach ($c in (Parse-EFISignatureList -Bytes (Get-SecureBootUEFI -Name KEK -ErrorAction Stop).Bytes)) {
|
$kekBytes = (Get-SecureBootUEFI -Name KEK -ErrorAction Stop).Bytes
|
||||||
$s=$c.Subject; $info=Convert-CertToInfo $c
|
foreach ($cert in (Parse-EFISignatureList -Bytes $kekBytes)) {
|
||||||
if ($s -like '*KEK CA 2011*') { $st.KEK.Has2011=$true; $st.KEK.Certs2011+=$info }
|
$subj = $cert.Subject
|
||||||
if ($s -like "*$CN_KEK2023*" -or $s -like '*KEK*CA 2023*') { $st.KEK.Has2023=$true; $st.KEK.Certs2023+=$info }
|
$info = Convert-CertToInfo $cert
|
||||||
|
if ($subj -like '*KEK CA 2011*') { $st.KEK.Has2011=$true; $st.KEK.Certs2011+=$info }
|
||||||
|
if ($subj -like "*$CN_KEK2023*" -or $subj -like '*KEK*CA 2023*') { $st.KEK.Has2023=$true; $st.KEK.Certs2023+=$info }
|
||||||
}
|
}
|
||||||
} catch { $st.KEK.Error=$_.Exception.Message }
|
} catch { $st.KEK.Error=$_.Exception.Message }
|
||||||
|
|
||||||
try {
|
try {
|
||||||
foreach ($c in (Parse-EFISignatureList -Bytes (Get-SecureBootUEFI -Name db -ErrorAction Stop).Bytes)) {
|
$dbBytes = (Get-SecureBootUEFI -Name db -ErrorAction Stop).Bytes
|
||||||
$s=$c.Subject; $info=Convert-CertToInfo $c
|
foreach ($cert in (Parse-EFISignatureList -Bytes $dbBytes)) {
|
||||||
if ($s -like '*UEFI CA 2011*') { $st.DB.Has2011UEFI=$true; $st.DB.Certs2011+=$info }
|
$subj = $cert.Subject
|
||||||
if ($s -like '*Windows Production PCA 2011*' -or $s -like '*Windows PCA 2011*') { $st.DB.Has2011WindowsPCA=$true; $st.DB.Certs2011+=$info }
|
$info = Convert-CertToInfo $cert
|
||||||
if ($s -like "*$CN_UEFI2023*" -and $s -notlike '*Option ROM*' -and $s -notlike '*Windows UEFI*') { $st.DB.Has2023UEFI=$true; $st.DB.Certs2023+=$info }
|
if ($subj -like '*UEFI CA 2011*') { $st.DB.Has2011UEFI=$true; $st.DB.Certs2011+=$info }
|
||||||
if ($s -like "*$CN_OPTROM2023*") { $st.DB.Has2023OptionROM=$true; $st.DB.Certs2023+=$info }
|
if ($subj -like '*Windows Production PCA 2011*' -or $subj -like '*Windows PCA 2011*') { $st.DB.Has2011WindowsPCA=$true; $st.DB.Certs2011+=$info }
|
||||||
if ($s -like "*$CN_WINUEFI2023*") { $st.DB.Has2023WindowsUEFI=$true; $st.DB.Certs2023+=$info }
|
if ($subj -like "*$CN_UEFI2023*" -and $subj -notlike '*Option ROM*' -and $subj -notlike '*Windows UEFI*') { $st.DB.Has2023UEFI=$true; $st.DB.Certs2023+=$info }
|
||||||
|
if ($subj -like "*$CN_OPTROM2023*") { $st.DB.Has2023OptionROM=$true; $st.DB.Certs2023+=$info }
|
||||||
|
if ($subj -like "*$CN_WINUEFI2023*") { $st.DB.Has2023WindowsUEFI=$true; $st.DB.Certs2023+=$info }
|
||||||
}
|
}
|
||||||
} catch { $st.DB.Error=$_.Exception.Message }
|
} catch { $st.DB.Error=$_.Exception.Message }
|
||||||
# ASCII fallback (kdyby X.509 parse selhal) — jen doplní booleany
|
# ASCII fallback (kdyby X.509 parse selhal) — jen doplní booleany
|
||||||
@@ -348,8 +371,8 @@ function Get-AvailableUpdatesText {
|
|||||||
if ($null -eq $v) { return '(nenastaveno)' }
|
if ($null -eq $v) { return '(nenastaveno)' }
|
||||||
$iv=[int]$v; $hex='0x{0:X}' -f $iv
|
$iv=[int]$v; $hex='0x{0:X}' -f $iv
|
||||||
$n = switch ($iv) {
|
$n = switch ($iv) {
|
||||||
0 {'vše hotovo (proces dokončen)'} 0x4000 {'vše aplikováno — čeká na finální restart'}
|
0 {'vše hotovo (proces dokončen)'} 0x4000 {'vše aplikováno — task čistí guard bit'}
|
||||||
0x4100 {'boot manager 2023 nasazen na ESP'} 0x4104 {'Microsoft UEFI CA 2023 v DB'}
|
0x4100 {'boot manager 2023 nasazen na ESP — čeká na RESTART'} 0x4104 {'Microsoft UEFI CA 2023 v DB'}
|
||||||
0x5104 {'Option ROM UEFI CA 2023 v DB'} 0x5904 {'Windows UEFI CA 2023 v DB'}
|
0x5104 {'Option ROM UEFI CA 2023 v DB'} 0x5904 {'Windows UEFI CA 2023 v DB'}
|
||||||
0x5944 {'naplánována plná sada (start)'} default {'zbývá aplikovat'} }
|
0x5944 {'naplánována plná sada (start)'} default {'zbývá aplikovat'} }
|
||||||
return "$hex ($n)"
|
return "$hex ($n)"
|
||||||
@@ -362,9 +385,22 @@ function Get-EventLogStatus {
|
|||||||
$events = Get-WinEvent -FilterHashtable @{ LogName='System'; Id=$ids } -MaxEvents 40 -ErrorAction Stop
|
$events = Get-WinEvent -FilterHashtable @{ LogName='System'; Id=$ids } -MaxEvents 40 -ErrorAction Stop
|
||||||
if ($events) {
|
if ($events) {
|
||||||
$sorted = $events | Sort-Object TimeCreated -Descending
|
$sorted = $events | Sort-Object TimeCreated -Descending
|
||||||
foreach ($id in $ids) { $x=$sorted | Where-Object {$_.Id -eq $id} | Select-Object -First 1; if ($x){ $e.ById[$id]=@{Time=$x.TimeCreated.ToString('yyyy-MM-dd HH:mm:ss')} } }
|
# Index posledního výskytu každého ID
|
||||||
foreach ($x in ($sorted|Select-Object -First 8)) { $e.RelevantEvents += [ordered]@{ EventId=$x.Id; TimeCreated=$x.TimeCreated.ToString('yyyy-MM-dd HH:mm:ss'); Level=$x.LevelDisplayName } }
|
foreach ($id in $ids) {
|
||||||
$last=$sorted|Select-Object -First 1; $e.LastEventId=$last.Id; $e.LastEventTime=$last.TimeCreated.ToString('yyyy-MM-dd HH:mm:ss')
|
$hit = $sorted | Where-Object { $_.Id -eq $id } | Select-Object -First 1
|
||||||
|
if ($hit) { $e.ById[$id] = @{ Time=$hit.TimeCreated.ToString('yyyy-MM-dd HH:mm:ss') } }
|
||||||
|
}
|
||||||
|
# Posledních 8 událostí pro -Detailed výpis
|
||||||
|
foreach ($ev in ($sorted | Select-Object -First 8)) {
|
||||||
|
$e.RelevantEvents += [ordered]@{
|
||||||
|
EventId = $ev.Id
|
||||||
|
TimeCreated = $ev.TimeCreated.ToString('yyyy-MM-dd HH:mm:ss')
|
||||||
|
Level = $ev.LevelDisplayName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$last = $sorted | Select-Object -First 1
|
||||||
|
$e.LastEventId = $last.Id
|
||||||
|
$e.LastEventTime = $last.TimeCreated.ToString('yyyy-MM-dd HH:mm:ss')
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
if ($_.CategoryInfo.Reason -ne 'NoMatchingEventsException') { $e.Error=$_.Exception.Message }
|
if ($_.CategoryInfo.Reason -ne 'NoMatchingEventsException') { $e.Error=$_.Exception.Message }
|
||||||
@@ -412,19 +448,27 @@ function Build-Phases {
|
|||||||
param($Sb, $Cert, $Boot, $TaskExists, $Reg, $Evt)
|
param($Sb, $Cert, $Boot, $TaskExists, $Reg, $Evt)
|
||||||
$e1795=$Evt.ById[1795]; $e1796=$Evt.ById[1796]
|
$e1795=$Evt.ById[1795]; $e1796=$Evt.ById[1796]
|
||||||
$cap=$Reg.WindowsUEFICA2023Capable
|
$cap=$Reg.WindowsUEFICA2023Capable
|
||||||
$bmActive = ($cap -eq 2) -or ($null -ne $Evt.ById[1808]) -or ($null -ne $Evt.ById[1799])
|
# Pouze Capable=2 je autoritativní signál aktivního Boot Manageru 2023.
|
||||||
|
# Eventy 1808/1799 nejsou Done signálem — mohou být ze starých/předchozích běhů a jsou nespolehlivé.
|
||||||
|
$bmActive = ($cap -eq 2)
|
||||||
$bmStaged = [bool]$Boot.EspHas2023
|
$bmStaged = [bool]$Boot.EspHas2023
|
||||||
|
# UEFICA2023Status musí být "Updated" pro potvrzení, že cert servisování proběhlo.
|
||||||
|
# Pokud klíč zcela chybí (starý build / BeforeServicing), netlumíme — BUILD_OUTDATED to zachytí jinak.
|
||||||
|
$statusUpdated = ($Reg.UEFICA2023StatusText -eq 'Updated')
|
||||||
|
$statusAbsent = ($null -eq $Reg.UEFICA2023Status)
|
||||||
|
$certServicingDone = $statusUpdated -or $statusAbsent
|
||||||
|
|
||||||
$ph=[ordered]@{}
|
$ph=[ordered]@{}
|
||||||
function _p($req,$done,$label){ [ordered]@{ Req=$req; Done=[bool]$done; Label=$label; State=$null; Note=$null } }
|
function _p($req,$done,$label){ [ordered]@{ Req=$req; Done=[bool]$done; Label=$label; State=$null; Note=$null } }
|
||||||
$ph['SecureBootEnabled'] = _p $true $Sb.IsEnabled 'Secure Boot zapnutý'
|
$ph['SecureBootEnabled'] = _p $true $Sb.IsEnabled 'Secure Boot zapnutý'
|
||||||
$ph['TaskExists'] = _p $true $TaskExists 'Servicing task k dispozici'
|
$ph['TaskExists'] = _p $true $TaskExists 'Servicing task k dispozici'
|
||||||
$ph['Kek2023'] = _p $true $Cert.KEK.Has2023 "KEK: Microsoft Corporation $CN_KEK2023"
|
$ph['Kek2023'] = _p $true $Cert.KEK.Has2023 "KEK: Microsoft Corporation $CN_KEK2023"
|
||||||
$ph['Db2023Windows'] = _p $true $Cert.DB.Has2023WindowsUEFI "DB: $CN_WINUEFI2023"
|
$ph['Db2023Windows'] = _p $true $Cert.DB.Has2023WindowsUEFI "DB: $CN_WINUEFI2023"
|
||||||
$ph['BootManager2023'] = _p $true $bmActive 'Boot Manager aktivní (bootuje se z Windows UEFI CA 2023)'
|
$ph['CertServicingStatus'] = _p $true $certServicingDone 'Certifikáty aktualizovány (UEFICA2023Status=Updated)'
|
||||||
$ph['Db2023ThirdParty'] = _p $false $Cert.DB.Has2023UEFI "DB: $CN_UEFI2023 (3rd-party, volitelné)"
|
$ph['BootManager2023'] = _p $true $bmActive 'Boot Manager aktivní (bootuje se z Windows UEFI CA 2023)'
|
||||||
$ph['Db2023OptionRom'] = _p $false $Cert.DB.Has2023OptionROM "DB: $CN_OPTROM2023 (volitelné)"
|
$ph['Db2023ThirdParty'] = _p $false $Cert.DB.Has2023UEFI "DB: $CN_UEFI2023 (3rd-party, volitelné)"
|
||||||
$ph['DbxRevoked'] = _p $false $Cert.DbxRevokesPCA2011 'DBX: revokace starého boot manageru 2011 (volitelné)'
|
$ph['Db2023OptionRom'] = _p $false $Cert.DB.Has2023OptionROM "DB: $CN_OPTROM2023 (volitelné)"
|
||||||
|
$ph['DbxRevoked'] = _p $false $Cert.DbxRevokesPCA2011 'DBX: revokace starého boot manageru 2011 (volitelné)'
|
||||||
|
|
||||||
$kekDb = $ph['Kek2023'].Done -and $ph['Db2023Windows'].Done
|
$kekDb = $ph['Kek2023'].Done -and $ph['Db2023Windows'].Done
|
||||||
$arrow = [char]0x2190
|
$arrow = [char]0x2190
|
||||||
@@ -438,10 +482,26 @@ function Build-Phases {
|
|||||||
elseif ($e1795) { $p.State='Fail'; $p.Note='chyba firmwaru (Event 1795)' }
|
elseif ($e1795) { $p.State='Fail'; $p.Note='chyba firmwaru (Event 1795)' }
|
||||||
else { $p.State='Pending'; $p.Note='zbývá nasadit' }
|
else { $p.State='Pending'; $p.Note='zbývá nasadit' }
|
||||||
}
|
}
|
||||||
|
'CertServicingStatus' {
|
||||||
|
$p.State = 'Pending'
|
||||||
|
if ($Reg.UEFICA2023StatusText -eq 'InProgress') {
|
||||||
|
$p.Note = 'servisování probíhá — task ještě nedoběhl nebo čeká na restart'
|
||||||
|
} elseif ($Reg.UEFICA2023StatusText -eq 'NotStarted') {
|
||||||
|
$p.Note = 'task zatím nezahájil servisování certifikátů'
|
||||||
|
} elseif ($Reg.UEFICA2023StatusText -eq 'KeyNotPresent') {
|
||||||
|
$p.Note = 'hodnota UEFICA2023Status není nastavena (starý build nebo před první remediací)'
|
||||||
|
} else {
|
||||||
|
$p.Note = "UEFICA2023Status: $($Reg.UEFICA2023StatusText)"
|
||||||
|
}
|
||||||
|
}
|
||||||
'BootManager2023' {
|
'BootManager2023' {
|
||||||
if ($bmStaged) { $p.State='Pending'; $p.Note="$arrow nasazen na ESP — vyžaduje RESTART" }
|
if ($bmStaged -or ($null -ne $cap -and [int]$cap -ge 1)) {
|
||||||
elseif ($kekDb) { $p.State='Pending'; $p.Note="$arrow zbývá — vyžaduje RESTART" }
|
$p.State='Pending'; $p.Note="$arrow cert v DB (Capable=$cap) — aktivuje se RESTARTEM"
|
||||||
else { $p.State='Pending'; $p.Note='až po nasazení KEK + DB' }
|
} elseif ($kekDb) {
|
||||||
|
$p.State='Pending'; $p.Note="$arrow zbývá — vyžaduje RESTART"
|
||||||
|
} else {
|
||||||
|
$p.State='Pending'; $p.Note='až po nasazení KEK + DB'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
default {
|
default {
|
||||||
if ($e1795) { $p.State='Fail'; $p.Note='chyba firmwaru (Event 1795)' } else { $p.State='Pending'; $p.Note='zbývá nasadit' }
|
if ($e1795) { $p.State='Fail'; $p.Note='chyba firmwaru (Event 1795)' } else { $p.State='Pending'; $p.Note='zbývá nasadit' }
|
||||||
@@ -456,14 +516,23 @@ function Get-RemediationCategory {
|
|||||||
$sb=$Result.SecureBoot; $reg=$Result.Registry; $evt=$Result.EventLog
|
$sb=$Result.SecureBoot; $reg=$Result.Registry; $evt=$Result.EventLog
|
||||||
$env=$Result.EnvironmentType; $ph=$Result.Phases; $cert=$Result.Certificates; $mode=$Result.OperatingMode
|
$env=$Result.EnvironmentType; $ph=$Result.Phases; $cert=$Result.Certificates; $mode=$Result.OperatingMode
|
||||||
|
|
||||||
|
# ── Základní blokátory ────────────────────────────────────────────────────
|
||||||
if (-not $sb.IsUEFI -or -not $sb.IsSupported) {
|
if (-not $sb.IsUEFI -or -not $sb.IsSupported) {
|
||||||
if ($env -like '*VM*') { return @{ Code='NO_SECUREBOOT_VM'; Tag='[X]'; Color='DarkGray'; Label='Secure Boot nepodporováno (VM bez vTPM/UEFI)' } }
|
$lbl = if ($env -like '*VM*') { 'Secure Boot nepodporováno (VM bez vTPM/UEFI)' } else { 'Secure Boot nepodporováno (Legacy BIOS)' }
|
||||||
return @{ Code='NO_SECUREBOOT'; Tag='[X]'; Color='DarkGray'; Label='Secure Boot nepodporováno (Legacy BIOS)' }
|
$cod = if ($env -like '*VM*') { 'NO_SECUREBOOT_VM' } else { 'NO_SECUREBOOT' }
|
||||||
|
return @{ Code=$cod; Tag='[X]'; Color='DarkGray'; Label=$lbl }
|
||||||
|
}
|
||||||
|
if (-not $sb.IsEnabled) {
|
||||||
|
return @{ Code='SECUREBOOT_DISABLED'; Tag='[OFF]'; Color='Yellow'; Label='Secure Boot vypnuto' }
|
||||||
|
}
|
||||||
|
if ($mode -eq 'Setup') {
|
||||||
|
return @{ Code='SETUP_MODE'; Tag='[!]'; Color='Magenta'; Label='Secure Boot v Setup Mode — aktualizaci nelze dokončit (chybí enrolled PK)' }
|
||||||
|
}
|
||||||
|
if (-not $ph['TaskExists'].Done) {
|
||||||
|
return @{ Code='TASK_MISSING'; Tag='[!]'; Color='Magenta'; Label='Chybí servicing task — nainstalujte aktuální kumulativní update (min. build 10/2025)' }
|
||||||
}
|
}
|
||||||
if (-not $sb.IsEnabled) { return @{ Code='SECUREBOOT_DISABLED'; Tag='[OFF]'; Color='Yellow'; Label='Secure Boot vypnuto' } }
|
|
||||||
if ($mode -eq 'Setup') { return @{ Code='SETUP_MODE'; Tag='[!]'; Color='Magenta'; Label='Secure Boot v Setup Mode — aktualizaci nelze dokončit (chybí enrolled PK)' } }
|
|
||||||
if (-not $ph['TaskExists'].Done) { return @{ Code='TASK_MISSING'; Tag='[!]'; Color='Magenta'; Label='Chybí servicing task — nainstalujte aktuální kumulativní update (min. build 10/2025)' } }
|
|
||||||
|
|
||||||
|
# ── Chyby a blokování ─────────────────────────────────────────────────────
|
||||||
$errEvt = $reg.UEFICA2023ErrorEvent -and ([int]$reg.UEFICA2023ErrorEvent -ne 0)
|
$errEvt = $reg.UEFICA2023ErrorEvent -and ([int]$reg.UEFICA2023ErrorEvent -ne 0)
|
||||||
if ($evt.ById[1795] -or $errEvt -or $reg.UEFICA2023StatusText -eq 'Failed') {
|
if ($evt.ById[1795] -or $errEvt -or $reg.UEFICA2023StatusText -eq 'Failed') {
|
||||||
return @{ Code='UPDATE_FAILED'; Tag='[FAIL]'; Color='Red'; Label='Selhání aktualizace (chyba firmwaru / UEFICA2023ErrorEvent)' }
|
return @{ Code='UPDATE_FAILED'; Tag='[FAIL]'; Color='Red'; Label='Selhání aktualizace (chyba firmwaru / UEFICA2023ErrorEvent)' }
|
||||||
@@ -475,28 +544,38 @@ function Get-RemediationCategory {
|
|||||||
return @{ Code='FIRMWARE_UPDATE_NEEDED'; Tag='[FW]'; Color='Magenta'; Label='Aktualizace pozastavena (známý problém) — zkontrolujte firmware u OEM' }
|
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í
|
# ── Starý build: certy jsou v KEK/DB, ale chybí servicing infrastruktura pro Boot Manager ──
|
||||||
# (WindowsUEFICA2023Capable i UEFICA2023Status absentní) a Boot Manager není aktivní → OS nemá
|
# WindowsUEFICA2023Capable i UEFICA2023Status zcela chybí → Windows Update nutný.
|
||||||
# 2023-podepsaný boot manager ani jeho servicing. Restarty nepomohou, pomůže jen Windows Update.
|
|
||||||
$certApplied = $ph['Kek2023'].Done -or $ph['Db2023Windows'].Done
|
$certApplied = $ph['Kek2023'].Done -or $ph['Db2023Windows'].Done
|
||||||
$auNow = [int]$reg.AvailableUpdates
|
$auNow = [int]$reg.AvailableUpdates
|
||||||
$bmStaged = ([bool]$Result.BootManager.EspHas2023) -or ($auNow -eq 0x4100) -or ($auNow -eq 0x4000)
|
$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)) {
|
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.' }
|
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')
|
# ── Fázové vyhodnocení ────────────────────────────────────────────────────
|
||||||
$missing=@($req|Where-Object{ -not $ph[$_].Done }); $missingLabels=@($missing|ForEach-Object{ $ph[$_].Label })
|
$req = @('Kek2023','Db2023Windows','CertServicingStatus','BootManager2023')
|
||||||
$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
|
$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
|
||||||
|
|
||||||
if ($missing.Count -eq 0) {
|
if ($missing.Count -eq 0) {
|
||||||
if ($cert.AnyExpiring2011) { return @{ Code='OK_TRANSITION'; Tag="[$SYM_DONE]"; Color='Green'; Label='HOTOVO — 2023 certifikáty i aktivní Boot Manager (staré 2011 ještě přítomné, což je normální)' } }
|
if ($cert.AnyExpiring2011) {
|
||||||
|
return @{ Code='OK_TRANSITION'; Tag="[$SYM_DONE]"; Color='Green'
|
||||||
|
Label='HOTOVO — 2023 certifikáty i aktivní Boot Manager (staré 2011 ještě přítomné, což je normální)' }
|
||||||
|
}
|
||||||
return @{ Code='OK'; Tag="[$SYM_DONE]"; Color='Green'; Label='HOTOVO — kompletní 2023 sada, aktivní Boot Manager, 2011 odstraněny' }
|
return @{ Code='OK'; Tag="[$SYM_DONE]"; Color='Green'; Label='HOTOVO — kompletní 2023 sada, aktivní Boot Manager, 2011 odstraněny' }
|
||||||
}
|
}
|
||||||
if ($missing.Count -eq 1 -and $missing[0] -eq 'BootManager2023') {
|
if ($missing.Count -eq 1 -and $missing[0] -eq 'BootManager2023') {
|
||||||
return @{ Code='UPDATE_PENDING'; Tag='[~]'; Color='Yellow'; Label='KEK i DB hotové — zbývá aktivovat Boot Manager: vyžaduje RESTART' }
|
return @{ Code='UPDATE_PENDING'; Tag='[~]'; Color='Yellow'
|
||||||
|
Label='UEFICA2023Status=Updated, KEK i DB hotové — zbývá aktivovat Boot Manager: vyžaduje RESTART' }
|
||||||
|
}
|
||||||
|
if ($anyApplied) {
|
||||||
|
return @{ Code='UPDATE_PARTIAL'; Tag='[~]'; Color='Cyan'
|
||||||
|
Label='Probíhá po částech — zbývá: ' + ($missingLabels -join '; ') }
|
||||||
}
|
}
|
||||||
if ($anyApplied) { return @{ Code='UPDATE_PARTIAL'; Tag='[~]'; Color='Cyan'; Label=('Probíhá po částech — zbývá: ' + ($missingLabels -join '; ')) } }
|
|
||||||
return @{ Code='UPDATE_NEEDED'; Tag='[!]'; Color='Yellow'; Label='Nutná aktualizace certifikátů' }
|
return @{ Code='UPDATE_NEEDED'; Tag='[!]'; Color='Yellow'; Label='Nutná aktualizace certifikátů' }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -506,30 +585,58 @@ function Get-RemediationCategory {
|
|||||||
|
|
||||||
function Invoke-Detection {
|
function Invoke-Detection {
|
||||||
param([bool]$SkipBootMgrFile)
|
param([bool]$SkipBootMgrFile)
|
||||||
|
|
||||||
$os = Get-CimInstance Win32_OperatingSystem -ErrorAction SilentlyContinue
|
$os = Get-CimInstance Win32_OperatingSystem -ErrorAction SilentlyContinue
|
||||||
$R = [ordered]@{
|
$osBuildFull = try {
|
||||||
AuditTimestamp=(Get-Date).ToString('yyyy-MM-dd HH:mm:ss'); Hostname=$env:COMPUTERNAME
|
$cv = Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion' -ErrorAction Stop
|
||||||
OSCaption=if($os){$os.Caption}else{$null}; OSBuild=if($os){$os.BuildNumber}else{$null}
|
'{0}.{1}' -f $cv.CurrentBuildNumber, $cv.UBR
|
||||||
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{'?'} })
|
} catch {
|
||||||
EnvironmentType=Get-EnvironmentType; Hardware=Get-HardwareInfo; SecureBoot=Get-SecureBootState
|
if ($os) { [string]$os.BuildNumber } else { '?' }
|
||||||
OperatingMode='Unknown'; BitLocker=$null; Certificates=$null; Registry=Get-RegistryStatus
|
|
||||||
EventLog=Get-EventLogStatus; TaskExists=Get-TaskExists; BootManager=$null; Phases=$null
|
|
||||||
Category=$null; CategoryLabel=$null; CategoryColor=$null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$R = [ordered]@{
|
||||||
|
AuditTimestamp = (Get-Date).ToString('yyyy-MM-dd HH:mm:ss')
|
||||||
|
Hostname = $env:COMPUTERNAME
|
||||||
|
OSCaption = if ($os) { $os.Caption } else { $null }
|
||||||
|
OSBuildFull = $osBuildFull
|
||||||
|
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
|
||||||
|
Category = $null
|
||||||
|
CategoryLabel = $null
|
||||||
|
CategoryColor = $null
|
||||||
|
}
|
||||||
|
|
||||||
if ($R.SecureBoot.IsUEFI -and $R.SecureBoot.IsSupported) {
|
if ($R.SecureBoot.IsUEFI -and $R.SecureBoot.IsSupported) {
|
||||||
$R.OperatingMode = Get-SecureBootMode
|
$R.OperatingMode = Get-SecureBootMode
|
||||||
$R.Certificates = Get-CertificateStatus
|
$R.Certificates = Get-CertificateStatus
|
||||||
$R.BootManager = Get-BootManagerStatus -SkipFileCheck:$SkipBootMgrFile
|
$R.BootManager = Get-BootManagerStatus -SkipFileCheck:$SkipBootMgrFile
|
||||||
} else {
|
} else {
|
||||||
$R.Certificates = [ordered]@{ KEK=[ordered]@{Has2011=$false;Has2023=$false;Certs2011=@();Certs2023=@();Error='SB nedostupný'}
|
$R.Certificates = [ordered]@{
|
||||||
DB=[ordered]@{Has2011UEFI=$false;Has2011WindowsPCA=$false;Has2023UEFI=$false;Has2023OptionROM=$false;Has2023WindowsUEFI=$false;Certs2011=@();Certs2023=@();Error='SB nedostupný'}
|
KEK = [ordered]@{ Has2011=$false; Has2023=$false; Certs2011=@(); Certs2023=@(); Error='SB nedostupný' }
|
||||||
DbxRevokesPCA2011=$false;AnyExpiring2011=$false }
|
DB = [ordered]@{ Has2011UEFI=$false; Has2011WindowsPCA=$false; Has2023UEFI=$false
|
||||||
$R.BootManager=[ordered]@{ EspHas2023=$false;Evidence=@();Chain=$null;Signer=$null;Error='SB nedostupný';Checked=$false }
|
Has2023OptionROM=$false; Has2023WindowsUEFI=$false
|
||||||
|
Certs2011=@(); Certs2023=@(); Error='SB nedostupný' }
|
||||||
|
DbxRevokesPCA2011=$false; AnyExpiring2011=$false
|
||||||
|
}
|
||||||
|
$R.BootManager = [ordered]@{ EspHas2023=$false; Evidence=@(); Chain=$null; Signer=$null; Error='SB nedostupný'; Checked=$false }
|
||||||
}
|
}
|
||||||
|
|
||||||
$R.BitLocker = Get-BitLockerInfo
|
$R.BitLocker = Get-BitLockerInfo
|
||||||
$R.Phases = Build-Phases -Sb $R.SecureBoot -Cert $R.Certificates -Boot $R.BootManager -TaskExists $R.TaskExists -Reg $R.Registry -Evt $R.EventLog
|
$R.Phases = Build-Phases -Sb $R.SecureBoot -Cert $R.Certificates -Boot $R.BootManager `
|
||||||
|
-TaskExists $R.TaskExists -Reg $R.Registry -Evt $R.EventLog
|
||||||
$cat = Get-RemediationCategory -Result $R
|
$cat = Get-RemediationCategory -Result $R
|
||||||
$R.Category=$cat.Code; $R.CategoryLabel="$($cat.Tag) $($cat.Label)"; $R.CategoryColor=$cat.Color
|
$R.Category = $cat.Code
|
||||||
|
$R.CategoryLabel = "$($cat.Tag) $($cat.Label)"
|
||||||
|
$R.CategoryColor = $cat.Color
|
||||||
return $R
|
return $R
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -545,13 +652,17 @@ function Show-DetectionSummary {
|
|||||||
Write-KV 'Stroj' $R.Hostname 'White' ("· {0} (build {1})" -f $R.OSCaption, $R.OSBuildFull)
|
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)
|
Write-KV 'Prostředí' $R.EnvironmentType 'White' ("· {0} {1}" -f $hw.Manufacturer, $hw.Model)
|
||||||
if ($sb.IsUEFI -and $sb.IsSupported) {
|
if ($sb.IsUEFI -and $sb.IsSupported) {
|
||||||
$modeColor = if ($R.OperatingMode -eq 'Setup') {'Red'} elseif ($R.OperatingMode -in @('User','Deployed')) {'White'} else {'Yellow'}
|
$modeColor = if ($R.OperatingMode -eq 'Setup') { 'Red' } elseif ($R.OperatingMode -in @('User','Deployed')) { 'White' } else { 'Yellow' }
|
||||||
Write-KV 'Secure Boot' ("zapnuto · mode {0}" -f $R.OperatingMode) $(if($sb.IsEnabled){'White'}else{'Yellow'}) $(if($R.OperatingMode -eq 'Setup'){'· Setup Mode = nelze dokončit!'})
|
$modeNote = if ($R.OperatingMode -eq 'Setup') { '· Setup Mode = nelze dokončit!' } else { $null }
|
||||||
|
Write-KV 'Secure Boot' ("zapnuto · mode {0}" -f $R.OperatingMode) $modeColor $modeNote
|
||||||
}
|
}
|
||||||
if ($bl -and $bl.Status -in @('On','Off','1','2','0')) {
|
if ($bl -and $bl.Status -in @('On','Off','1','2','0')) {
|
||||||
$blOn = ($bl.Status -eq 'On' -or $bl.Status -eq '1' -or $bl.Status -eq '2')
|
$blOn = ($bl.Status -eq 'On' -or $bl.Status -eq '1' -or $bl.Status -eq '2')
|
||||||
$note = if ($blOn -and $bl.UsesPcr) { '· PCR/TPM — před restartem ověř recovery key!' } elseif ($blOn) { '· chráněno' } else { '' }
|
$blRisk = $blOn -and $bl.UsesPcr
|
||||||
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'})
|
$blNote = if ($blRisk) { '· PCR/TPM — před restartem ověř recovery key!' } elseif ($blOn) { '· chráněno' } else { '' }
|
||||||
|
$blColor = if ($blRisk) { 'Yellow' } else { 'White' }
|
||||||
|
$noteColor = if ($blRisk) { 'Yellow' } else { 'DarkGray' }
|
||||||
|
Write-KV 'BitLocker' ("{0} [{1}]" -f $bl.Status, $bl.Protectors) $blColor $blNote $noteColor
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($Prereqs) { Show-Prerequisites -Prereqs $Prereqs }
|
if ($Prereqs) { Show-Prerequisites -Prereqs $Prereqs }
|
||||||
@@ -611,42 +722,41 @@ function Show-DetailedDetection {
|
|||||||
|
|
||||||
function Resolve-RemediationPlan {
|
function Resolve-RemediationPlan {
|
||||||
param($R, [bool]$ForceMode)
|
param($R, [bool]$ForceMode)
|
||||||
$plan=[ordered]@{ Applicable=$false; AskUser=$false; Caution=$null; Reason=$null; NextSteps=@() }
|
$plan=[ordered]@{ Applicable=$false; Caution=$null; Reason=$null; NextSteps=@() }
|
||||||
switch -Wildcard ($R.Category) {
|
switch -Wildcard ($R.Category) {
|
||||||
'OK' { $plan.Reason='Server je kompletně hotový — žádná akce.'; if ($ForceMode){ $plan.Applicable=$true; $plan.AskUser=$true } }
|
'OK' { $plan.Reason='Server je kompletně hotový — žádná akce.'; if ($ForceMode){ $plan.Applicable=$true } }
|
||||||
'OK_TRANSITION' {
|
'OK_TRANSITION' {
|
||||||
$plan.Reason='2023 certifikáty i aktivní Boot Manager jsou nasazeny. Staré 2011 zůstávají (normální). Žádná akce.'
|
$plan.Reason='2023 certifikáty i aktivní Boot Manager jsou nasazeny. Staré 2011 zůstávají (normální). Žádná akce.'
|
||||||
$plan.NextSteps=@('Nepovinné: časem ověřte revokaci starého boot manageru (DBX).')
|
$plan.NextSteps=@('Nepovinné: časem ověřte revokaci starého boot manageru (DBX).')
|
||||||
if ($ForceMode){ $plan.Applicable=$true; $plan.AskUser=$true }
|
if ($ForceMode){ $plan.Applicable=$true }
|
||||||
}
|
}
|
||||||
'UPDATE_NEEDED' { $plan.Applicable=$true; $plan.AskUser=$true; $plan.Reason='Lze zahájit aktualizaci metodou registry (KB5068202).' }
|
'UPDATE_NEEDED' { $plan.Applicable=$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_PARTIAL' { $plan.Applicable=$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' {
|
'UPDATE_PENDING' {
|
||||||
$au = [int]$R.Registry.AvailableUpdates
|
$au = [int]$R.Registry.AvailableUpdates
|
||||||
# „Staged" = Boot Manager je už připravený k aktivaci (2023 na ESP, nebo AvailableUpdates
|
# 0x4100 = Boot Manager je staged na ESP — stačí RESTART, task nespouštět.
|
||||||
# v boot-manager fázi 0x4100/0x4000). Pak STAČÍ RESTART. Jinak je nutné ho teprve naplánovat.
|
# Vše ostatní (0x4000, 0x0, …) — spustit task, který posune proces dál.
|
||||||
$staged = ([bool]$R.BootManager.EspHas2023) -or ($au -eq 0x4100) -or ($au -eq 0x4000)
|
if ($au -eq 0x4100) {
|
||||||
if ($staged) {
|
$plan.Reason = 'Boot Manager 2023 je staged (AvailableUpdates=0x4100). Aktivuje se RESTARTEM — task spouštět netřeba.'
|
||||||
$plan.Reason='Boot Manager je připravený (staged). Aktivuje se SAMOTNÝM RESTARTEM — víc nastavovat netřeba.'
|
$plan.NextSteps = @('RESTARTUJTE server.', 'BitLocker (PCR7): před restartem ověřte recovery key.', 'Po restartu spusťte kontrolu znovu.')
|
||||||
$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 {
|
} else {
|
||||||
$plan.Applicable=$true; $plan.AskUser=$true
|
$plan.Applicable = $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.Reason = ('KEK i DB hotové, Boot Manager v procesu (AvailableUpdates=0x{0:X}). Task posune proces dál.' -f $au)
|
||||||
$plan.Caution='Tímto se naplánuje Boot Manager; aktivuje se až RESTARTEM (re-aplikace už hotových certifikátů je neškodná).'
|
$plan.Caution = 'Po dokončení tasku bude nutný RESTART pro aktivaci Boot Manageru.'
|
||||||
$plan.NextSteps=@('Po tomto kroku RESTARTUJTE server.','Po restartu spusťte kontrolu znovu (nebo -RegisterResume).')
|
$plan.NextSteps = @('Po dokončení tasku RESTARTUJTE server.', 'Po restartu spusťte kontrolu znovu.')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
'UPDATE_FAILED' {
|
'UPDATE_FAILED' {
|
||||||
if ($R.EventLog.ById[1795]) {
|
if ($R.EventLog.ById[1795]) {
|
||||||
$plan.Reason='Selhání Event 1795 (chyba firmwaru). Příčina je ve firmwaru/hostiteli.'
|
$plan.Reason='Selhání Event 1795 (chyba firmwaru). Příčina je ve firmwaru/hostiteli.'
|
||||||
$plan.NextSteps=@('Aktualizujte firmware / Hyper-V hostitele (KB5085790).','Poté spusťte kontrolu znovu.')
|
$plan.NextSteps=@('Aktualizujte firmware / Hyper-V hostitele (KB5085790).','Poté spusťte kontrolu znovu.')
|
||||||
if ($ForceMode){ $plan.Applicable=$true; $plan.AskUser=$true; $plan.Caution='Event 1795 = problém firmwaru; -Force jen zopakuje pokus.' }
|
if ($ForceMode){ $plan.Applicable=$true; $plan.Caution='Event 1795 = problém firmwaru; -Force jen zopakuje pokus.' }
|
||||||
} else { $plan.Applicable=$true; $plan.AskUser=$true; $plan.Reason='Předchozí pokus selhal. Lze zopakovat.'; $plan.Caution='Zkontrolujte UEFICA2023ErrorEvent výše.' }
|
} else { $plan.Applicable=$true; $plan.Reason='Předchozí pokus selhal. Lze zopakovat.'; $plan.Caution='Zkontrolujte UEFICA2023ErrorEvent výše.' }
|
||||||
}
|
}
|
||||||
'FIRMWARE_UPDATE_NEEDED' {
|
'FIRMWARE_UPDATE_NEEDED' {
|
||||||
$plan.Reason='Aktualizace pozastavena (Temporarily Paused / Event 1802) — známý problém firmwaru.'
|
$plan.Reason='Aktualizace pozastavena (Temporarily Paused / Event 1802) — známý problém firmwaru.'
|
||||||
$plan.NextSteps=@('Aktualizujte firmware u výrobce (Dell/HP/Lenovo/Supermicro).','Poté spusťte kontrolu znovu.')
|
$plan.NextSteps=@('Aktualizujte firmware u výrobce (Dell/HP/Lenovo/Supermicro).','Poté spusťte kontrolu znovu.')
|
||||||
if ($ForceMode){ $plan.Applicable=$true; $plan.AskUser=$true; $plan.Caution='Pozastaveno firmwarem; -Force se pokusí i tak.' }
|
if ($ForceMode){ $plan.Applicable=$true; $plan.Caution='Pozastaveno firmwarem; -Force se pokusí i tak.' }
|
||||||
}
|
}
|
||||||
'NOT_SUPPORTED' {
|
'NOT_SUPPORTED' {
|
||||||
$plan.Reason='ConfidenceLevel: Not Supported — zařízení nepodporuje automatickou cestu.'
|
$plan.Reason='ConfidenceLevel: Not Supported — zařízení nepodporuje automatickou cestu.'
|
||||||
@@ -684,6 +794,37 @@ function Get-UserConsent {
|
|||||||
} catch { return ((Read-Host 'Aplikovat? [a/N]') -match '^(a|ano|y|yes)$') }
|
} catch { return ((Read-Host 'Aplikovat? [a/N]') -match '^(a|ano|y|yes)$') }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function Invoke-RestartOffer {
|
||||||
|
# Nabídne uživateli restart. Skript nikdy nerestartuje automaticky — vždy interaktivní dotaz.
|
||||||
|
if (-not [Environment]::UserInteractive) {
|
||||||
|
Write-Line ' (Neinteraktivní relace — restart proveďte ručně.)' DarkGray
|
||||||
|
return
|
||||||
|
}
|
||||||
|
Write-Host ''
|
||||||
|
Write-Host ' Chcete nyní restartovat server? (výchozí: Ne)' -ForegroundColor White
|
||||||
|
try {
|
||||||
|
$yn = New-Object System.Management.Automation.Host.ChoiceDescription '&Ano', 'Restartovat server nyní'
|
||||||
|
$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) {
|
||||||
|
Add-LogLine 'Restart: uživatel potvrdil — spouštím Restart-Computer'
|
||||||
|
Flush-Log
|
||||||
|
Restart-Computer -Force
|
||||||
|
} else {
|
||||||
|
Write-Host ' Restart odložen — restartujte server ručně co nejdříve.' -ForegroundColor Gray
|
||||||
|
Add-LogLine 'Restart: uživatel odložil restart'
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
if ((Read-Host ' Restartovat nyní? [a/N]') -match '^(a|ano|y|yes)$') {
|
||||||
|
Add-LogLine 'Restart: uživatel potvrdil restart (fallback Read-Host)'
|
||||||
|
Flush-Log
|
||||||
|
Restart-Computer -Force
|
||||||
|
} else {
|
||||||
|
Write-Line ' Restart odložen — restartujte server ručně co nejdříve.' Gray
|
||||||
|
Add-LogLine 'Restart: uživatel odložil restart (fallback Read-Host)'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region ── Stav napříč restarty + RunOnce ─────────────────────────────────────
|
#region ── Stav napříč restarty + RunOnce ─────────────────────────────────────
|
||||||
@@ -715,33 +856,30 @@ function Register-Resume {
|
|||||||
|
|
||||||
function Invoke-Remediation {
|
function Invoke-Remediation {
|
||||||
param([bool]$SkipBootMgrFile)
|
param([bool]$SkipBootMgrFile)
|
||||||
$out=[ordered]@{ Status='Unknown'; Message=''; After=$null; LogFile=$script:LogFile }
|
$out=[ordered]@{ Status='Unknown'; Message=''; After=$null }
|
||||||
Write-Head 'REMEDIACE'; Add-LogLine 'REMEDIACE START'
|
Write-Head 'REMEDIACE'; Add-LogLine 'REMEDIACE START'
|
||||||
|
|
||||||
# Přečti AvailableUpdates ještě PŘED zápisem — rozhodnutí, jestli ho nastavit nebo zachovat.
|
# AvailableUpdates se nastavuje na 0x5944 POUZE jednou — při prvním spuštění (null) nebo po
|
||||||
$auBefore = (Get-ItemProperty $REG_SECUREBOOT -ErrorAction SilentlyContinue).AvailableUpdates
|
# dokončení předchozího cyklu (0). Systém si pak hodnotu sám spravuje: odečítá bity jak postupuje
|
||||||
# "In progress" = nenulová hodnota → systém ji spravuje sám (dle KB5068202 nastavit jen jednou).
|
# (0x5944→0x5904→0x5104→0x4104→0x4100→0x4000→0x0). Skript ji neresetuje — jen čte a interpretuje.
|
||||||
# Null nebo 0 → první start nebo re-trigger (Boot Manager není aktivní přesto, že certifikáty jsou OK).
|
$auCurrent = (Get-ItemProperty $REG_SECUREBOOT -ErrorAction SilentlyContinue).AvailableUpdates
|
||||||
$auInProgress = ($null -ne $auBefore) -and ([int]$auBefore -ne 0)
|
$auNeedsInit = ($null -eq $auCurrent) -or ([int]$auCurrent -eq 0)
|
||||||
$auLabel = if ($auInProgress) { '0x{0:X} zachováno (probíhá)' -f [int]$auBefore } else { '0x5944 (nové/reset)' }
|
$auInfo = if ($auNeedsInit) { '0x5944 (první inicializace)' } else { '0x{0:X} zachováno (probíhá)' -f [int]$auCurrent }
|
||||||
Write-Host (" [1/3] Nastavuji registry (MicrosoftUpdateManagedOptIn=1, AvailableUpdates={0}) ... " -f $auLabel) -NoNewline
|
Write-Host (" [1/3] Nastavuji registry (MicrosoftUpdateManagedOptIn=1, AvailableUpdates={0}) ... " -f $auInfo) -NoNewline
|
||||||
try {
|
try {
|
||||||
if (-not (Test-Path $REG_SECUREBOOT)) { New-Item -Path $REG_SECUREBOOT -Force -ErrorAction Stop | Out-Null }
|
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 'MicrosoftUpdateManagedOptIn' -Value 1 -Type DWord -Force -ErrorAction Stop
|
||||||
# AvailableUpdates nastavujeme na 0x5944 POUZE pokud ještě nebyl nastaven (null) nebo je 0.
|
if ($auNeedsInit) {
|
||||||
# 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
|
Set-ItemProperty -Path $REG_SECUREBOOT -Name 'AvailableUpdates' -Value $AVAILABLE_UPDATES_VALUE -Type DWord -Force -ErrorAction Stop
|
||||||
|
$vCheck = (Get-ItemProperty $REG_SECUREBOOT -ErrorAction Stop).AvailableUpdates
|
||||||
|
if ([int]$vCheck -ne $AVAILABLE_UPDATES_VALUE) { throw ('Ověření selhalo — AvailableUpdates=0x{0:X}, očekáváno 0x5944' -f [int]$vCheck) }
|
||||||
}
|
}
|
||||||
$o = Get-ItemProperty $REG_SECUREBOOT -Name 'HighConfidenceOptOut' -ErrorAction SilentlyContinue
|
$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 ($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 }
|
if (-not (Test-Path $REG_SERVICING)) { New-Item -Path $REG_SERVICING -Force -ErrorAction Stop | Out-Null }
|
||||||
$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
|
Write-Host 'hotovo' -ForegroundColor Green
|
||||||
$auAction = if ($auInProgress) { 'zachováno' } else { 'nastaveno' }
|
$auLogVal = if ($auNeedsInit) { '0x5944 (nastaveno)' } else { '0x{0:X} (zachováno)' -f [int]$auCurrent }
|
||||||
Add-LogLine ('Krok 1: MicrosoftUpdateManagedOptIn=1, AvailableUpdates=0x{0:X} ({1})' -f [int]$vNow, $auAction)
|
Add-LogLine ("Krok 1: MicrosoftUpdateManagedOptIn=1, AvailableUpdates={0}" -f $auLogVal)
|
||||||
} 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 }
|
} 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 }
|
if ($SkipScheduledTask) { Write-Line ' [2/3] Servicing task přeskočen (-SkipScheduledTask).' DarkGray }
|
||||||
@@ -773,7 +911,16 @@ function Invoke-Remediation {
|
|||||||
$after=Invoke-Detection -SkipBootMgrFile:$SkipBootMgrFile
|
$after=Invoke-Detection -SkipBootMgrFile:$SkipBootMgrFile
|
||||||
Write-Host 'hotovo' -ForegroundColor Green; Write-Host ''
|
Write-Host 'hotovo' -ForegroundColor Green; Write-Host ''
|
||||||
Write-Line (' AvailableUpdates: {0}' -f (Get-AvailableUpdatesText $after.Registry.AvailableUpdates)) Cyan
|
Write-Line (' AvailableUpdates: {0}' -f (Get-AvailableUpdatesText $after.Registry.AvailableUpdates)) Cyan
|
||||||
foreach ($k in @('Kek2023','Db2023Windows','BootManager2023')) { $p=$after.Phases[$k]; Write-Check -State $p.State -Text $p.Label -Note $p.Note }
|
Write-Line (' UEFICA2023Status : {0}' -f $after.Registry.UEFICA2023StatusText) $(if($after.Registry.UEFICA2023StatusText -eq 'Updated'){'Green'}else{'White'})
|
||||||
|
foreach ($k in @('Kek2023','Db2023Windows','CertServicingStatus','BootManager2023')) { $p=$after.Phases[$k]; Write-Check -State $p.State -Text $p.Label -Note $p.Note }
|
||||||
|
$afterAU = [int]$after.Registry.AvailableUpdates
|
||||||
|
if ($afterAU -eq 0x4100) {
|
||||||
|
Write-Host ''
|
||||||
|
Write-Host ' *** Boot Manager 2023 je staged (AvailableUpdates=0x4100) ***' -ForegroundColor Yellow
|
||||||
|
Write-Host ' Aktivace proběhne po restartu serveru.' -ForegroundColor Yellow
|
||||||
|
Add-LogLine 'Post-remediace: AU=0x4100 — BM staged, vyžaduje restart'
|
||||||
|
Invoke-RestartOffer
|
||||||
|
}
|
||||||
$out.Status='Applied'; $out.After=$after; $out.Message='Registry nastavena, task zpracován. Boot Manager se aktivuje až po restartu.'
|
$out.Status='Applied'; $out.After=$after; $out.Message='Registry nastavena, task zpracován. Boot Manager se aktivuje až po restartu.'
|
||||||
Add-LogLine 'REMEDIACE KONEC: Applied'
|
Add-LogLine 'REMEDIACE KONEC: Applied'
|
||||||
return $out
|
return $out
|
||||||
@@ -791,8 +938,8 @@ function Get-StageInfo {
|
|||||||
0x5904 { return @{ Step=2; Total=6; Label='Windows UEFI CA 2023 v DB' } }
|
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' } }
|
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' } }
|
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' } }
|
0x4100 { return @{ Step=5; Total=6; Label='Boot Manager nasazen na ESP — RESTART' } }
|
||||||
0x4000 { return @{ Step=6; Total=6; Label='čeká na finální restart' } }
|
0x4000 { return @{ Step=6; Total=6; Label='guard bit — task dokončuje' } }
|
||||||
0 { return @{ Step=6; Total=6; Label='dokončeno' } }
|
0 { return @{ Step=6; Total=6; Label='dokončeno' } }
|
||||||
default { return @{ Step=1; Total=6; Label=('mezistav 0x{0:X}' -f [int]$v) } }
|
default { return @{ Step=1; Total=6; Label=('mezistav 0x{0:X}' -f [int]$v) } }
|
||||||
}
|
}
|
||||||
@@ -821,21 +968,52 @@ function Show-Progress {
|
|||||||
|
|
||||||
function Get-Prerequisites {
|
function Get-Prerequisites {
|
||||||
param($R, [bool]$IsAdmin)
|
param($R, [bool]$IsAdmin)
|
||||||
$sb = $R.SecureBoot
|
$sb = $R.SecureBoot
|
||||||
$list = @()
|
$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='Spuštěno jako Administrator'
|
||||||
$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'}) }
|
Ok=$IsAdmin; Hard=$true
|
||||||
$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'}) }
|
Note=if(-not $IsAdmin){'spusťte PowerShell „Run as administrator"'} }
|
||||||
$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 }
|
$sbOk = $sb.IsUEFI -and $sb.IsSupported
|
||||||
# Boot-manager servicing přítomný? Když jsou certy v DB/KEK, ale WindowsUEFICA2023Capable i
|
$list += @{ Label='UEFI + Secure Boot podporováno'
|
||||||
# UEFICA2023Status chybí a boot manager není aktivní → build je příliš starý (nutný Windows Update).
|
Ok=$sbOk; Hard=$true
|
||||||
$auP = [int]$R.Registry.AvailableUpdates
|
Note=if(-not $sbOk){'Legacy BIOS / nepodporováno'} }
|
||||||
|
|
||||||
|
$list += @{ Label='Secure Boot zapnutý'
|
||||||
|
Ok=[bool]$sb.IsEnabled; Hard=$true
|
||||||
|
Note=if($sbOk -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 }
|
||||||
|
|
||||||
|
# Starý build: certy jsou v KEK/DB, ale WindowsUEFICA2023Capable + UEFICA2023Status zcela chybí
|
||||||
|
# a BM není staged → servicing infrastruktura pro Boot Manager neexistuje, nutný Windows Update.
|
||||||
|
$auP = [int]$R.Registry.AvailableUpdates
|
||||||
$bmStagedP = ([bool]$R.BootManager.EspHas2023) -or ($auP -eq 0x4100) -or ($auP -eq 0x4000)
|
$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)
|
$staleServ = ($R.Phases['Kek2023'].Done -or $R.Phases['Db2023Windows'].Done) `
|
||||||
$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"}) }
|
-and (-not $R.Phases['BootManager2023'].Done) `
|
||||||
$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'}) }
|
-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"} }
|
||||||
|
|
||||||
|
$notSupported = $R.Registry.ConfidenceLevel -like 'Not Supported*'
|
||||||
|
$list += @{ Label='Zařízení není „Not Supported"'
|
||||||
|
Ok=(-not $notSupported); Hard=$false
|
||||||
|
Note=if($notSupported){'ConfidenceLevel: Not Supported — řešení u OEM'} }
|
||||||
|
|
||||||
return $list
|
return $list
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -901,6 +1079,10 @@ $remediation = $null
|
|||||||
if (-not $plan.Applicable) {
|
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++ } }
|
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 }
|
elseif ($result.Category -like 'OK*') { Write-Host ' Není potřeba žádná akce.' -ForegroundColor Green }
|
||||||
|
# UPDATE_PENDING na 0x4100: Boot Manager staged — nabídnout restart přímo z detekce
|
||||||
|
if ($result.Category -eq 'UPDATE_PENDING' -and -not $CheckOnly -and -not $isWhatIf -and $isAdmin) {
|
||||||
|
if ([int]$result.Registry.AvailableUpdates -eq 0x4100) { Invoke-RestartOffer }
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if ($plan.Caution) { Write-Host (" Pozor: {0}" -f $plan.Caution) -ForegroundColor Yellow }
|
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 }
|
if (-not $isAdmin) { Write-Host ''; Write-Host ' Remediaci nelze provést bez práv Administrator.' -ForegroundColor Yellow }
|
||||||
@@ -928,7 +1110,7 @@ if ($remediation -and $remediation.Status -eq 'Applied') {
|
|||||||
else {
|
else {
|
||||||
Write-Host ' ČÁSTEČNĚ HOTOVO — proces pokračuje po restartu.' -ForegroundColor Yellow
|
Write-Host ' ČÁSTEČNĚ HOTOVO — proces pokračuje po restartu.' -ForegroundColor Yellow
|
||||||
Write-Host ''; Write-Host ' Další kroky:' -ForegroundColor White
|
Write-Host ''; Write-Host ' Další kroky:' -ForegroundColor White
|
||||||
Write-Host ' 1. Naplánujte RESTART serveru (skript jej záměrně neprovedl).' -ForegroundColor Gray
|
Write-Host ' 1. RESTART serveru (byl nabídnut výše; pokud jste odmítli, restartujte ručně).' -ForegroundColor Gray
|
||||||
if ($after.BitLocker -and $after.BitLocker.UsesPcr -and ($after.BitLocker.Status -eq 'On' -or $after.BitLocker.Status -eq '1' -or $after.BitLocker.Status -eq '2')) {
|
if ($after.BitLocker -and $after.BitLocker.UsesPcr -and ($after.BitLocker.Status -eq 'On' -or $after.BitLocker.Status -eq '1' -or $after.BitLocker.Status -eq '2')) {
|
||||||
Write-Host ' 2. BitLocker (PCR/TPM) je aktivní — před restartem ověřte recovery key!' -ForegroundColor Yellow
|
Write-Host ' 2. BitLocker (PCR/TPM) je aktivní — před restartem ověřte recovery key!' -ForegroundColor Yellow
|
||||||
} else { Write-Host ' 2. BitLocker s PCR7: před restartem ověřte recovery key.' -ForegroundColor Gray }
|
} else { Write-Host ' 2. BitLocker s PCR7: před restartem ověřte recovery key.' -ForegroundColor Gray }
|
||||||
|
|||||||
Reference in New Issue
Block a user