Clean repo

This commit is contained in:
Petr Stepan
2026-06-08 15:38:04 +02:00
parent ee4f8db4ea
commit 2712030aea
4 changed files with 141 additions and 1259 deletions
-679
View File
@@ -1,679 +0,0 @@
#Requires -Version 5.1
<#
.SYNOPSIS
Audituje stav Secure Boot certifikátů na Windows Serveru.
.DESCRIPTION
Detekuje podporu Secure Boot, stav povolení, přítomnost certifikátů (expirující 2011
vs. nové 2023 náhrady), stav v registrech a relevantní záznamy v Event Logu.
Vrací strukturovaný JSON výstup + human-readable souhrn.
Skript nevyžaduje žádné externí moduly (pure PowerShell 5.1+).
Toleruje prostředí bez Secure Boot (Legacy BIOS, Gen1 VM apod.).
Spouštění na vzdáleném serveru:
Invoke-Command -ComputerName SERVER01 -FilePath .\Invoke-SecureBootAudit.ps1
Spouštění lokálně s uložením výsledku:
.\Invoke-SecureBootAudit.ps1 -OutputPath C:\Audit\result.json
.PARAMETER OutputPath
Cesta pro uložení JSON výsledku. Pokud není zadána, JSON se nevypisuje na konzoli
(použijte -JsonOnly nebo -PassThru pro programatické zpracování).
.PARAMETER JsonOnly
Vypíše pouze JSON na stdout, bez human-readable souhrnue.
.PARAMETER PassThru
Vrátí výsledek jako PowerShell objekt (vhodné pro Invoke-Command pipeline).
.EXAMPLE
.\Invoke-SecureBootAudit.ps1
.EXAMPLE
.\Invoke-SecureBootAudit.ps1 -OutputPath C:\Audit\SERVER01.json
.EXAMPLE
Invoke-Command -ComputerName SERVER01 -FilePath .\Invoke-SecureBootAudit.ps1 -ArgumentList $null,$false,$true
.NOTES
Vyžaduje spuštění jako Administrator pro přístup k UEFI databázím a Event Logu.
Relevantní KB: KB5062710, KB5068202, KB5085046
#>
[CmdletBinding()]
param(
[string]$OutputPath,
[switch]$JsonOnly,
[switch]$PassThru
)
$ErrorActionPreference = 'SilentlyContinue'
#region ── Helper functions ──────────────────────────────────────────────────
function Get-EnvironmentType {
try {
$cs = Get-WmiObject Win32_ComputerSystem -ErrorAction Stop
$mfr = [string]$cs.Manufacturer
$model = [string]$cs.Model
if ($mfr -like '*VMware*') { return 'VMware VM' }
if ($mfr -like '*Microsoft*' -and $model -eq 'Virtual Machine') { return 'Hyper-V VM' }
if ($model -like '*Virtual*' -or $mfr -like '*QEMU*' -or $mfr -like '*Xen*') {
return 'Other VM'
}
return 'Physical'
} catch {
return 'Unknown'
}
}
function Get-HardwareInfo {
$hw = [ordered]@{
Manufacturer = 'Unknown'
Model = 'Unknown'
SerialNumber = 'Unknown'
FirmwareType = 'Unknown'
BiosVersion = 'Unknown'
BiosReleaseDate = 'Unknown'
}
try {
$cs = Get-WmiObject Win32_ComputerSystem -ErrorAction Stop
$hw.Manufacturer = [string]$cs.Manufacturer
$hw.Model = [string]$cs.Model
} catch {}
try {
$bios = Get-WmiObject Win32_BIOS -ErrorAction Stop
$hw.BiosVersion = [string]$bios.SMBIOSBIOSVersion
$hw.SerialNumber = [string]$bios.SerialNumber
if ($bios.ReleaseDate) {
$hw.BiosReleaseDate = [Management.ManagementDateTimeConverter]::ToDateTime(
$bios.ReleaseDate).ToString('yyyy-MM-dd')
}
} catch {}
# Firmware type — primárně z registry, fallback přes přítomnost SecureBoot klíče
$fwTypeKey = Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control' `
-Name PEFirmwareType -ErrorAction SilentlyContinue
if ($fwTypeKey) {
$hw.FirmwareType = if ($fwTypeKey.PEFirmwareType -eq 2) { 'UEFI' } else { 'Legacy BIOS' }
} elseif (Test-Path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecureBoot') {
$hw.FirmwareType = 'UEFI'
} else {
$hw.FirmwareType = 'Legacy BIOS'
}
return $hw
}
function Get-SecureBootState {
$state = [ordered]@{
IsUEFI = $false
IsSupported = $false
IsEnabled = $false
ConfirmResult = 'Unknown'
Error = $null
}
$fwTypeKey = Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control' `
-Name PEFirmwareType -ErrorAction SilentlyContinue
$state.IsUEFI = ($fwTypeKey -and $fwTypeKey.PEFirmwareType -eq 2) `
-or (Test-Path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecureBoot')
if (-not $state.IsUEFI) { return $state }
try {
$result = Confirm-SecureBootUEFI -ErrorAction Stop
$state.IsSupported = $true
$state.IsEnabled = [bool]$result
$state.ConfirmResult = $result.ToString()
} catch {
$msg = $_.Exception.Message
if ($msg -like '*not supported*' -or $msg -like '*Cmdlet not supported*') {
$state.IsSupported = $false
$state.ConfirmResult = 'NotSupported'
} elseif ($msg -like '*disabled*') {
$state.IsSupported = $true
$state.IsEnabled = $false
$state.ConfirmResult = 'Disabled'
} else {
$state.IsSupported = $true
$state.IsEnabled = $false
$state.ConfirmResult = "Error"
$state.Error = $msg
}
}
return $state
}
function Parse-EFISignatureList {
<#
.SYNOPSIS
Parsuje EFI_SIGNATURE_LIST strukturu a vrátí X.509 certifikáty.
#>
param([byte[]]$Bytes)
$certs = @()
if (-not $Bytes -or $Bytes.Length -lt 28) { return $certs }
# EFI_CERT_X509_GUID {a5c059a1-94e4-4aa7-87b5-ab155c2bf072} v little-endian
$X509_GUID = [byte[]](
0xa1,0x59,0xc0,0xa5, # Data1 LE
0xe4,0x94, # Data2 LE
0xa7,0x4a, # Data3 LE
0x87,0xb5,0xab,0x15,0x5c,0x2b,0xf0,0x72 # Data4 BE
)
$offset = 0
while ($offset + 28 -le $Bytes.Length) {
$sigTypeGUID = $Bytes[$offset..($offset + 15)]
$sigListSize = [BitConverter]::ToUInt32($Bytes, $offset + 16)
$sigHeaderSize = [BitConverter]::ToUInt32($Bytes, $offset + 20)
$sigSize = [BitConverter]::ToUInt32($Bytes, $offset + 24)
if ($sigListSize -lt 28 -or $sigListSize -gt ($Bytes.Length - $offset)) { break }
# Porovnat GUID
$isX509 = $true
for ($i = 0; $i -lt 16; $i++) {
if ($sigTypeGUID[$i] -ne $X509_GUID[$i]) { $isX509 = $false; break }
}
if ($isX509 -and $sigSize -gt 16) {
$sigOffset = $offset + 28 + $sigHeaderSize
$listEnd = $offset + $sigListSize
while ($sigOffset + $sigSize -le $listEnd) {
# Přeskočit 16 B SignatureOwner GUID, zbytek je DER certifikát
$certOffset = $sigOffset + 16
$certSize = [int]$sigSize - 16
if ($certOffset + $certSize -le $Bytes.Length -and $certSize -gt 0) {
$certBytes = $Bytes[$certOffset..($certOffset + $certSize - 1)]
try {
$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2(
, [byte[]]$certBytes)
$certs += $cert
} catch { }
}
$sigOffset += $sigSize
}
}
$offset += $sigListSize
}
return $certs
}
function Convert-CertToInfo {
param([System.Security.Cryptography.X509Certificates.X509Certificate2]$Cert)
return [ordered]@{
Subject = $Cert.Subject
Thumbprint = $Cert.Thumbprint
NotBefore = $Cert.NotBefore.ToString('yyyy-MM-dd')
NotAfter = $Cert.NotAfter.ToString('yyyy-MM-dd')
}
}
function Get-CertificateStatus {
$status = [ordered]@{
KEK = [ordered]@{
Has2011 = $false
Has2023 = $false
Certs2011 = @()
Certs2023 = @()
Error = $null
}
DB = [ordered]@{
Has2011UEFI = $false
Has2011WindowsPCA = $false
Has2023UEFI = $false
Has2023OptionROM = $false
Has2023WindowsUEFI = $false
Certs2011 = @()
Certs2023 = @()
Error = $null
}
AnyExpiring2011 = $false
AllReplacements2023 = $false
AllReplacements2023_VM = $false
}
# ── KEK databáze ──
try {
$kekObj = Get-SecureBootUEFI -Name KEK -ErrorAction Stop
$kekCerts = Parse-EFISignatureList -Bytes $kekObj.Bytes
foreach ($cert in $kekCerts) {
$subj = $cert.Subject
$info = Convert-CertToInfo $cert
if ($subj -like '*KEK CA 2011*') {
$status.KEK.Has2011 = $true
$status.KEK.Certs2011 += $info
}
if ($subj -like '*KEK 2K CA 2023*' -or ($subj -like '*KEK*CA 2023*')) {
$status.KEK.Has2023 = $true
$status.KEK.Certs2023 += $info
}
}
} catch {
$status.KEK.Error = $_.Exception.Message
}
# ── DB databáze ──
try {
$dbObj = Get-SecureBootUEFI -Name db -ErrorAction Stop
$dbCerts = Parse-EFISignatureList -Bytes $dbObj.Bytes
foreach ($cert in $dbCerts) {
$subj = $cert.Subject
$info = Convert-CertToInfo $cert
# 2011 expirující certifikáty
if ($subj -like '*UEFI CA 2011*') {
$status.DB.Has2011UEFI = $true
$status.DB.Certs2011 += $info
}
if ($subj -like '*Windows Production PCA 2011*' -or $subj -like '*Windows PCA 2011*') {
$status.DB.Has2011WindowsPCA = $true
$status.DB.Certs2011 += $info
}
# 2023 náhradní certifikáty
if ($subj -like '*UEFI CA 2023*' -and
$subj -notlike '*Option ROM*' -and
$subj -notlike '*Windows UEFI*') {
$status.DB.Has2023UEFI = $true
$status.DB.Certs2023 += $info
}
if ($subj -like '*Option ROM UEFI CA 2023*') {
$status.DB.Has2023OptionROM = $true
$status.DB.Certs2023 += $info
}
if ($subj -like '*Windows UEFI CA 2023*') {
$status.DB.Has2023WindowsUEFI = $true
$status.DB.Certs2023 += $info
}
}
} catch {
$status.DB.Error = $_.Exception.Message
}
$status.AnyExpiring2011 = $status.KEK.Has2011 -or
$status.DB.Has2011UEFI -or
$status.DB.Has2011WindowsPCA
# Plná sada pro fyzické servery (všechny 4 certifikáty)
$status.AllReplacements2023 = $status.KEK.Has2023 -and
$status.DB.Has2023UEFI -and
$status.DB.Has2023OptionROM -and
$status.DB.Has2023WindowsUEFI
# Minimální sada pro VM (UEFI CA 2023 + Option ROM nejsou na VM povinné —
# VM nemá fyzické PCIe option ROM karty, hypervisor řídí UEFI obsah)
$status.AllReplacements2023_VM = $status.KEK.Has2023 -and
$status.DB.Has2023WindowsUEFI
return $status
}
function Get-RegistryStatus {
$reg = [ordered]@{
SecureBootEnabled = $null
AvailableUpdates = $null
HighConfidenceOptOut = $null
ServicingKeyExists = $false
UEFICA2023Status = $null
UEFICA2023StatusText = 'KeyNotPresent'
UEFICA2023Error = $null
WindowsUEFICA2023Capable = $null
}
# Hlavní klíč
$mainProps = Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\SecureBoot' `
-ErrorAction SilentlyContinue
if ($mainProps) {
$reg.SecureBootEnabled = $mainProps.SecureBootEnabled
$reg.AvailableUpdates = $mainProps.AvailableUpdates
$reg.HighConfidenceOptOut = $mainProps.HighConfidenceOptOut
}
# Servicing podklíč
$svcProps = Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\SecureBoot\Servicing' `
-ErrorAction SilentlyContinue
if ($svcProps) {
$reg.ServicingKeyExists = $true
$reg.UEFICA2023Status = $svcProps.UEFICA2023Status
$reg.UEFICA2023Error = $svcProps.UEFICA2023Error
$reg.WindowsUEFICA2023Capable = $svcProps.WindowsUEFICA2023Capable
$reg.UEFICA2023StatusText = switch ($reg.UEFICA2023Status) {
0 { 'NotStarted' }
1 { 'InProgress' }
2 { 'Success' }
3 { 'Failed' }
$null { 'KeyNotPresent' }
default { "Unknown ($($reg.UEFICA2023Status))" }
}
}
return $reg
}
function Get-EventLogStatus {
$evtStatus = [ordered]@{
LastEventId = $null
LastEventTime = $null
LastEventMessage = $null
ConfidenceLevel = 'NoRelevantEvents'
RelevantEvents = @()
Error = $null
}
$relevantIds = @(1795, 1796, 1800, 1801, 1802, 1803, 1808)
try {
$events = Get-WinEvent -FilterHashtable @{
LogName = 'System'
Id = $relevantIds
} -MaxEvents 20 -ErrorAction Stop
if ($events) {
$sorted = $events | Sort-Object TimeCreated -Descending
foreach ($evt in ($sorted | Select-Object -First 10)) {
$msgShort = ($evt.Message -replace '\s+', ' ').TrimStart()
$msgShort = if ($msgShort.Length -gt 250) { $msgShort.Substring(0, 250) + '...' } else { $msgShort }
$evtStatus.RelevantEvents += [ordered]@{
EventId = $evt.Id
TimeCreated = $evt.TimeCreated.ToString('yyyy-MM-dd HH:mm:ss')
Level = $evt.LevelDisplayName
Message = $msgShort
}
}
$last = $sorted | Select-Object -First 1
$evtStatus.LastEventId = $last.Id
$evtStatus.LastEventTime = $last.TimeCreated.ToString('yyyy-MM-dd HH:mm:ss')
$lastMsg = ($last.Message -replace '\s+', ' ').TrimStart()
$evtStatus.LastEventMessage = if ($lastMsg.Length -gt 300) {
$lastMsg.Substring(0, 300) + '...'
} else { $lastMsg }
$evtStatus.ConfidenceLevel = switch ($last.Id) {
1808 { 'HighConfidence-Success' }
1801 { 'HighConfidence-Failed' }
1795 { 'Failed-HyperVKnownIssue' }
1802 { 'Pending' }
1803 { 'InProgress' }
default { 'Informational' }
}
}
} catch {
$msg = $_.Exception.Message
if ($msg -like '*No events*' -or $_.CategoryInfo.Reason -eq 'NoMatchingEventsException') {
$evtStatus.ConfidenceLevel = 'NoRelevantEvents'
} else {
$evtStatus.Error = $msg
$evtStatus.ConfidenceLevel = 'EventLogError'
}
}
return $evtStatus
}
function Get-RemediationCategory {
param($Result)
$sb = $Result.SecureBoot
$cert = $Result.Certificates
$reg = $Result.Registry
$env = $Result.EnvironmentType
# Bez UEFI / Secure Boot není podporováno
if (-not $sb.IsUEFI -or -not $sb.IsSupported) {
if ($env -like '*VM*') {
return @{ Code = 'NO_SECUREBOOT_VM'; Emoji = '❌'; Label = 'Secure Boot nepodporováno (VM bez vTPM/UEFI)' }
}
return @{ Code = 'NO_SECUREBOOT'; Emoji = '❌'; Label = 'Secure Boot nepodporováno (Legacy BIOS)' }
}
if (-not $sb.IsEnabled) {
return @{ Code = 'SECUREBOOT_DISABLED'; Emoji = '⏸️'; Label = 'Secure Boot vypnuto' }
}
# Secure Boot je zapnutý — zkontrolovat certifikáty
# Na VM stačí minimální sada (KEK 2K CA 2023 + Windows UEFI CA 2023),
# UEFI CA 2023 a Option ROM UEFI CA 2023 nejsou na VM povinné
$isVM = $env -like '*VM*'
$certOK = if ($isVM) { $cert.AllReplacements2023_VM } else { $cert.AllReplacements2023 }
$certOKFull = $cert.AllReplacements2023
if ($certOK -and -not $cert.AnyExpiring2011) {
$_label = if ($isVM -and -not $certOKFull) {
'OK — má povinné 2023 certifikáty pro VM (UEFI CA + Option ROM nejsou na VM vyžadovány)'
} else {
'OK — má nové 2023 certifikáty'
}
return @{ Code = 'OK'; Emoji = '✅'; Label = $_label }
}
if ($certOK -and $cert.AnyExpiring2011) {
$_label = if ($isVM -and -not $certOKFull) {
'OK — přechodný stav, VM má povinné 2023 certifikáty'
} else {
'OK — přechodný stav (2023 i 2011 certifikáty)'
}
return @{ Code = 'OK_TRANSITION'; Emoji = '✅'; Label = $_label }
}
# Selhání aktualizace
if ($reg.UEFICA2023Status -eq 3 -or
$Result.EventLog.LastEventId -eq 1795 -or
$Result.EventLog.LastEventId -eq 1801) {
return @{ Code = 'UPDATE_FAILED'; Emoji = '🔴'; Label = 'Selhání aktualizace certifikátů' }
}
# Aktualizace právě probíhá / čeká na restart
if ($reg.UEFICA2023Status -eq 1 -or $reg.UEFICA2023Status -eq 2) {
return @{ Code = 'UPDATE_PENDING'; Emoji = '⏳'; Label = 'Aktualizace dokončena, čeká na restart' }
}
# Firmware nepodporuje nové certifikáty
if ($null -ne $reg.WindowsUEFICA2023Capable -and $reg.WindowsUEFICA2023Capable -eq 0) {
return @{ Code = 'FIRMWARE_UPDATE_NEEDED'; Emoji = '🔧'; Label = 'Čeká na firmware update (OEM)' }
}
# Nutná aktualizace certifikátů
return @{ Code = 'UPDATE_NEEDED'; Emoji = '⚠️'; Label = 'Nutná aktualizace certifikátů' }
}
#endregion
#region ── Main ──────────────────────────────────────────────────────────────
$auditStart = Get-Date
# Předpočítat hodnoty, které by způsobily parse error uvnitř hashtable (try/catch v PS 5.1)
$_fqdn = try { [Net.Dns]::GetHostEntry('').HostName } catch { $env:COMPUTERNAME }
$_osInfo = Get-WmiObject Win32_OperatingSystem -ErrorAction SilentlyContinue
$_osCaption = if ($_osInfo) { $_osInfo.Caption } else { $null }
$_osBuild = if ($_osInfo) { $_osInfo.BuildNumber } else { $null }
$result = [ordered]@{
AuditTimestamp = $auditStart.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")
Hostname = $env:COMPUTERNAME
FQDN = $_fqdn
OSCaption = $_osCaption
OSBuild = $_osBuild
Architecture = $env:PROCESSOR_ARCHITECTURE
EnvironmentType = Get-EnvironmentType
Hardware = Get-HardwareInfo
SecureBoot = Get-SecureBootState
Certificates = $null
Registry = Get-RegistryStatus
EventLog = Get-EventLogStatus
Category = $null
CategoryLabel = $null
RemediationAction = $null
}
# Certifikáty parsovat pouze pokud je Secure Boot dostupný
if ($result.SecureBoot.IsUEFI -and $result.SecureBoot.IsSupported) {
$result.Certificates = Get-CertificateStatus
} else {
$result.Certificates = [ordered]@{
KEK = [ordered]@{
Has2011 = $false; Has2023 = $false
Certs2011 = @(); Certs2023 = @()
Error = 'Secure Boot not available — certificate check skipped'
}
DB = [ordered]@{
Has2011UEFI = $false; Has2011WindowsPCA = $false
Has2023UEFI = $false; Has2023OptionROM = $false; Has2023WindowsUEFI = $false
Certs2011 = @(); Certs2023 = @()
Error = 'Secure Boot not available — certificate check skipped'
}
AnyExpiring2011 = $false
AllReplacements2023 = $false
AllReplacements2023_VM = $false
}
}
# Kategorie
$cat = Get-RemediationCategory -Result $result
$result.Category = $cat.Code
$result.CategoryLabel = "$($cat.Emoji) $($cat.Label)"
$remediationMap = @{
'OK' = 'Žádná akce'
'OK_TRANSITION' = 'Žádná akce (monitorovat dokud nezmizí 2011 certifikáty)'
'UPDATE_NEEDED' = 'Naplánovat rollout nových certifikátů (KB5068202)'
'UPDATE_FAILED' = 'Troubleshooting selhání — zkontrolovat Event 1795/1801 (KB5085046)'
'UPDATE_PENDING' = 'Provést restart serveru pro dokončení aktualizace'
'FIRMWARE_UPDATE_NEEDED'= 'Kontaktovat OEM / aktualizovat firmware serveru'
'SECUREBOOT_DISABLED' = 'Rozhodnutí o zapnutí Secure Boot (mimo scope skriptu)'
'NO_SECUREBOOT' = 'Dokumentovat jako výjimku — Legacy BIOS, Secure Boot nelze'
'NO_SECUREBOOT_VM' = 'Dokumentovat jako výjimku — VM bez Secure Boot (Gen1 / bez vTPM)'
}
$result.RemediationAction = $remediationMap[$result.Category]
$result.AuditDurationMs = [int](Get-Date).Subtract($auditStart).TotalMilliseconds
#endregion
#region ── Output ────────────────────────────────────────────────────────────
$jsonOutput = $result | ConvertTo-Json -Depth 10
if ($OutputPath) {
$jsonOutput | Out-File -FilePath $OutputPath -Encoding UTF8 -Force
Write-Host "JSON uložen: $OutputPath" -ForegroundColor Green
}
if (-not $JsonOnly) {
$sb = $result.SecureBoot
$cert = $result.Certificates
$reg = $result.Registry
$evt = $result.EventLog
$hw = $result.Hardware
$catColor = switch -Wildcard ($result.Category) {
'OK*' { 'Green' }
'UPDATE_NEEDED' { 'Yellow' }
'UPDATE_PENDING' { 'Cyan' }
'UPDATE_FAILED' { 'Red' }
'FIRMWARE*' { 'Magenta' }
'SECUREBOOT_DISABLED' { 'Yellow' }
default { 'Gray' }
}
$line = '=' * 60
Write-Host ''
Write-Host $line -ForegroundColor Cyan
Write-Host " SECURE BOOT AUDIT — $($result.Hostname)" -ForegroundColor Cyan
Write-Host " $($result.AuditTimestamp)" -ForegroundColor DarkGray
Write-Host $line -ForegroundColor Cyan
Write-Host ''
Write-Host 'SERVER' -ForegroundColor Yellow
Write-Host " Hostname : $($result.Hostname)"
Write-Host " FQDN : $($result.FQDN)"
Write-Host " OS : $($result.OSCaption) (Build $($result.OSBuild))"
Write-Host " Prostředí : $($result.EnvironmentType)"
Write-Host " Hardware : $($hw.Manufacturer) $($hw.Model) [S/N: $($hw.SerialNumber)]"
Write-Host " BIOS/FW : $($hw.BiosVersion) ($($hw.BiosReleaseDate))"
Write-Host " Firmware typ: $($hw.FirmwareType)"
Write-Host ''
Write-Host 'SECURE BOOT' -ForegroundColor Yellow
Write-Host " UEFI firmware : $($sb.IsUEFI)"
Write-Host " Podporováno : $($sb.IsSupported)"
Write-Host " Povoleno : $($sb.IsEnabled)"
if ($sb.Error) { Write-Host " ! Chyba : $($sb.Error)" -ForegroundColor Red }
Write-Host ''
Write-Host 'CERTIFIKÁTY' -ForegroundColor Yellow
$c = $cert
Write-Host " ── Expirující 2011 ──────────────────────────────────"
Write-Host " KEK CA 2011 (exp. 24.6.2026) : $(if ($c.KEK.Has2011) {'PŘÍTOMEN ⚠️'} else {'chybí'})"
Write-Host " DB UEFI CA 2011 (exp. 27.6.2026): $(if ($c.DB.Has2011UEFI) {'PŘÍTOMEN ⚠️'} else {'chybí'})"
Write-Host " DB Windows PCA 2011 (19.10.2026) : $(if ($c.DB.Has2011WindowsPCA) {'PŘÍTOMEN ⚠️'} else {'chybí'})"
Write-Host " ── Nové náhrady 2023 ────────────────────────────────"
Write-Host " KEK 2K CA 2023 : $(if ($c.KEK.Has2023) {'přítomen ✓'} else {'CHYBÍ'})"
Write-Host " DB UEFI CA 2023 : $(if ($c.DB.Has2023UEFI) {'přítomen ✓'} else {'CHYBÍ'})"
Write-Host " DB Option ROM UEFI CA 2023 : $(if ($c.DB.Has2023OptionROM) {'přítomen ✓'} else {'CHYBÍ'})"
Write-Host " DB Windows UEFI CA 2023 : $(if ($c.DB.Has2023WindowsUEFI) {'přítomen ✓'} else {'CHYBÍ'})"
if ($c.KEK.Error) { Write-Host " ! KEK chyba : $($c.KEK.Error)" -ForegroundColor Red }
if ($c.DB.Error) { Write-Host " ! DB chyba : $($c.DB.Error)" -ForegroundColor Red }
Write-Host ''
Write-Host 'REGISTRY STAV' -ForegroundColor Yellow
Write-Host " UEFICA2023Status : $($reg.UEFICA2023Status) ($($reg.UEFICA2023StatusText))"
Write-Host " AvailableUpdates : $($reg.AvailableUpdates)"
Write-Host " WindowsUEFICA2023Capable: $($reg.WindowsUEFICA2023Capable)"
Write-Host " HighConfidenceOptOut : $($reg.HighConfidenceOptOut)"
if ($reg.UEFICA2023Error) {
Write-Host " ! UEFICA2023Error : $($reg.UEFICA2023Error)" -ForegroundColor Red
}
Write-Host ''
Write-Host 'EVENT LOG (relevantní EventID: 1795/1796/1800-1803/1808)' -ForegroundColor Yellow
Write-Host " Poslední event : EventID $($evt.LastEventId) ($($evt.LastEventTime))"
Write-Host " Confidence level : $($evt.ConfidenceLevel)"
if ($evt.RelevantEvents.Count -gt 0) {
Write-Host " Posledních $([Math]::Min($evt.RelevantEvents.Count,5)) záznamů:"
foreach ($e in ($evt.RelevantEvents | Select-Object -First 5)) {
$ec = if ($e.EventId -eq 1808) { 'Green' }
elseif ($e.EventId -in @(1795,1801)) { 'Red' }
else { 'DarkGray' }
Write-Host " [$($e.TimeCreated)] EventID $($e.EventId) $($e.Level)" -ForegroundColor $ec
}
} else {
Write-Host ' Žádné relevantní záznamy nenalezeny.' -ForegroundColor DarkGray
}
Write-Host ''
Write-Host 'VÝSLEDEK' -ForegroundColor Yellow
Write-Host " $($result.CategoryLabel)" -ForegroundColor $catColor
Write-Host " Doporučená akce: $($result.RemediationAction)" -ForegroundColor White
Write-Host ''
Write-Host $line -ForegroundColor Cyan
Write-Host ''
}
if ($JsonOnly) {
Write-Output $jsonOutput
}
if ($PassThru) {
return $result
}
#endregion
+38
View File
@@ -1380,6 +1380,20 @@ $prereqs = Get-Prerequisites -R $result -IsAdmin $isAdmin
# Zobrazení stavu (4 sekce: předpoklady, certifikáty, registry, události)
Show-Status -R $result -Prereqs $prereqs
# ── 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.
if ($result.EnvironmentType -eq 'VMware VM' -and $result.SecureBoot.IsEnabled) {
Write-Host ''
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 ' │ 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 ' │ │' -ForegroundColor Yellow
Write-Host ' │ Postup: https://totalservice.atlassian.net/browse/KB-543 │' -ForegroundColor Cyan
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 ─────────────────────────────────────────────────
if ($CheckOnly) {
Write-Host ''; Write-Rule
@@ -1504,6 +1518,30 @@ if ($isWhatIf) {
$autoRestartNote = if ($AutoRestart) { 'ANO (-AutoRestart)' } else { 'NE (nabídne dotaz)' }
Write-Host (" - Restart serveru: {0}" -f $autoRestartNote) -ForegroundColor Gray
} else {
# ── Upozornění na BitLocker před souhlasem s remediací ───────────────────
# Zobrazuje se vždy, když je BitLocker aktivní — bez ohledu na typ protektoru.
$blPre = $result.BitLocker
$blPreActv = $blPre -and ($blPre.Status -eq 'On' -or $blPre.Status -eq '1' -or $blPre.Status -eq '2')
if ($blPreActv) {
Write-Host ''
if ($blPre.UsesPcr) {
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 ' │ 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 ' │ │' -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 ' └──────────────────────────────────────────────────────────────────────────┘' -ForegroundColor Yellow
} else {
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 ' │ Před zahájením remediace ověřte dostupnost recovery klíče (AD, trezor). │' -ForegroundColor Yellow
Write-Host ' └──────────────────────────────────────────────────────────────────────────┘' -ForegroundColor Yellow
}
Add-LogLine ("Upozornění: BitLocker aktivní před remediací (Protectors: {0}, PCR: {1})" -f $blPre.Protectors, $blPre.UsesPcr)
}
# ── Nabídnout remediaci ──────────────────────────────────────────────────
$question = switch ($result.Category) {
'UPDATE_NEEDED' { 'Server potřebuje aktualizaci Secure Boot certifikátů. Chcete zahájit proces?' }
+103 -61
View File
@@ -16,7 +16,15 @@ Microsoft v upozornění [KB5062710](https://support.microsoft.com/en-us/topic/w
**Dopad, pokud se nic neudělá:** server bude dál bootovat, ale přestane přijímat nové ochrany boot procesu — aktualizace Boot Manageru, revokace (DBX) a mitigace nově objevených boot-level zranitelností. Postupně se sníží ochrana a ovlivní to scénáře závislé na Secure Boot důvěře (BitLocker hardening, third-party bootloadery).
**Pozor — co je skutečné „hotovo":** nestačí jen mít nové certifikáty v KEK/DB. Aktualizace je dokončená, až když se systém **reálně bootuje z Boot Manageru podepsaného Windows UEFI CA 2023** (signalizuje `WindowsUEFICA2023Capable = 2` / Event 1808). Tento poslední krok se aplikuje **až po restartu**. Skript proto kontroluje i Boot Manager, ne jen certifikáty.
**Co je skutečné „hotovo":** nestačí jen mít nové certifikáty v KEK/DB — aktualizace je dokončená až když jsou splněny **všechny 4 podmínky** a Boot Manager je ověřen:
1. `Windows UEFI CA 2023` v DB + `Microsoft Corporation KEK 2K CA 2023` v KEK
2. `AvailableUpdates = 0x0` nebo `0x4000`
3. `UEFICA2023Status = "Updated"`
4. `UEFICA2023Error = 0` nebo neexistuje
5. Boot Manager: `WindowsUEFICA2023Capable = 2` nebo `certutil` ověřil podpis souboru `bootmgfw.efi`
Poslední krok (aktivace Boot Manageru) se aplikuje **až po restartu** a skript to ověří přímo na souboru.
---
@@ -26,11 +34,11 @@ Microsoft v upozornění [KB5062710](https://support.microsoft.com/en-us/topic/w
- **Windows PowerShell 5.1+**, spuštěno jako **Administrator**
- Build Windows z **14. 10. 2025 nebo novější** ([KB5066835](https://support.microsoft.com/en-us/topic/october-14-2025-kb5066835-os-builds-26200-6899-and-26100-6899-1db237d8-9f3b-4218-9515-3e0a32729685)) — bez něj neexistují servicing registry klíče ani úloha. Skript na to upozorní.
- Žádné externí moduly. UEFI databáze čte nativně.
- Žádné externí moduly. UEFI databáze a podpis bootloaderu čte nativně.
### Skript NIKDY nerestartuje server
Restart je nutný k dokončení aktualizace, ale necháváme ho **na vás** (plánované okno, ohled na BitLocker). Skript jen jasně řekne, kdy je restart potřeba.
Restart je nutný k dokončení aktualizace, ale necháváme ho **na vás** (plánované okno, ohled na BitLocker). Výjimka: parametr `-AutoRestart` pro automatizované scénáře.
### Typický průběh (interaktivně)
@@ -39,15 +47,15 @@ Restart je nutný k dokončení aktualizace, ale necháváme ho **na vás** (pl
.\Invoke-SecureBootRemediation.ps1
```
Skript provede detekci, ukáže **checklist** a u stavů, kde to dává smysl, se **zeptá**, zda remediaci aplikovat. Po souhlasu nastaví registry a spustí servicing úlohu (počká na ni). Pak:
Skript provede detekci, zobrazí výstup ve 4 sekcích a u stavů, kde to dává smysl, se **zeptá**, zda remediaci aplikovat. Po souhlasu nastaví registry a spustí servicing úlohu (počká na ni). Pak:
```text
2) Naplánujte RESTART serveru
3) Po restartu spusťte skript znovu
4) Opakujte, dokud checklist nebude celý zelený (HOTOVO)
4) Opakujte, dokud stav nebude HOTOVO
```
Aktualizace se aplikuje **po částech na více restartů** — hodnota `AvailableUpdates` postupně klesá `0x5944 → 0x5904 → 0x5104 → 0x4104 → 0x4100 → 0x4000 → 0x0`. Cílový stav je `AvailableUpdates=0x0`, `UEFICA2023Status=Updated`, `WindowsUEFICA2023Capable=2`.
Aktualizace se aplikuje **po částech na více restartů** — hodnota `AvailableUpdates` postupně klesá `0x5944 → 0x5904 → 0x5104 → 0x4104 → 0x4100 → 0x4000 → 0x0`.
### Důležité před restartem
@@ -57,63 +65,95 @@ Pokud má server **BitLocker s ochranou vázanou na TPM/PCR7**, změna Boot Mana
| Parametr | Účel |
|---|---|
| *(bez parametru)* | Interaktivní detekce → dotaz → remediace (bez restartu) |
| `-CheckOnly` | Jen detekce + checklist + **exit kód** (0 = hotovo, 1 = nutná akce, 2 = blokováno). Bez změn a bez dotazu. Vhodné pro RMM/monitoring. |
| `-WhatIf` | Ukáže, co by remediace udělala, bez provedení změn |
| `-AssumeYes` | Neinteraktivně aplikuje remediaci (pokud je smysluplná). Stále bez restartu. |
| `-Force` | Aplikuje i ve stavech, kdy to skript jinak nedoporučuje |
| `-SkipScheduledTask` | Nastaví registry, ale nespustí úlohu (spustí se sama, cca á 12 h) |
| `-SkipBootManagerFileCheck` | Vynechá mount ESP + `certutil`. Dokončení se i tak pozná z `WindowsUEFICA2023Capable=2` / Event 1808 / 1799. |
| `-RegisterResume` | Po remediaci vyžadující restart nastaví `RunOnce`, který po PŘÍŠTÍM (ručním) restartu sám spustí `-CheckOnly`. Nikdy nerestartuje. |
| `-Detailed` | Rozšířený rozpis (certifikáty, události, Boot Manager chain, bit-rozklad `AvailableUpdates`) |
| `-PassThru` | Vrátí výsledný objekt do pipeline |
| `-LogPath <cesta>` | Vlastní cesta k logu (jinak log vzniká vedle skriptu jen při reálné remediaci) |
| *(bez parametru)* | Interaktivní detekce → dotaz → remediace |
| `-CheckOnly` | Jen detekce + výpis + **exit kód** (0 = hotovo, 1 = nutná akce, 2 = blokováno). Bez změn a bez dotazu. Vhodné pro RMM/monitoring. |
| `-WhatIf` | Ukáže, co by remediace udělala, bez provedení změn. |
| `-AssumeYes` | Přeskočí interaktivní dotaz a remediaci rovnou aplikuje (pokud je smysluplná). |
| `-Force` | Spustí remediaci i v případě, že povinné certifikáty jsou již přítomny. |
| `-AutoRestart` | Po stavu `AU=0x4100` (Boot Manager staged, čeká na restart) restartuje server automaticky bez dotazu. **POZOR: okamžitý restart.** |
| `-EspDriveLetter <X>` | Písmeno jednotky pro dočasný mount ESP při ověřování Boot Manageru (výchozí `S`). Změňte, pokud `S:` je na serveru obsazeno. |
### Příklady
```powershell
# Jen kontrola stavu (pro skript/monitoring), bez zásahu
# Jen kontrola stavu pro skript/monitoring, bez zásahu
.\Invoke-SecureBootRemediation.ps1 -CheckOnly
# Náhled co by se stalo, bez změn
.\Invoke-SecureBootRemediation.ps1 -WhatIf
# Neinteraktivní aplikace + auto-kontrola po příštím restartu
.\Invoke-SecureBootRemediation.ps1 -AssumeYes -RegisterResume
# Neinteraktivní aplikace (CI/CD, dávkový rollout)
.\Invoke-SecureBootRemediation.ps1 -AssumeYes
# Plný rozpis pro diagnostiku
.\Invoke-SecureBootRemediation.ps1 -CheckOnly -Detailed
# Server kde S: je obsazeno, použít T: pro ESP
.\Invoke-SecureBootRemediation.ps1 -EspDriveLetter T
# Neinteraktivní + auto-restart po staged Boot Manageru
.\Invoke-SecureBootRemediation.ps1 -AssumeYes -AutoRestart
```
---
## 3. Co skript dělá (detailně)
### Fázový checklist
### Výstup — 4 sekce
Server je „HOTOVO" teprve když projdou všechny **povinné** fáze. Volitelné fáze jsou jen informativní.
Skript zobrazí vždy čtyři sekce:
```
PŘEDPOKLADY:
1. [+] Administrátorská práva
2. [+] Windows Server — Zjištěno: Windows Server 2022 Standard (build 20348.xxx)
3. [+] UEFI + Secure Boot zapnutý
4. [+] Úroveň záplat >= 10/2025 — stavové registry klíče přítomny
5. [+] Scheduled task 'Secure-Boot-Update' k dispozici
6. [+] PowerShell SecureBoot cmdlety — Get-SecureBootUEFI dostupný
CERTIFIKÁTY:
[+] Windows UEFI CA 2023 v DB (povinný) platný do 2045-06-10
[+] Microsoft Corporation KEK 2K CA 2023 (povinný)
[ ] Boot Manager (bootmgfw.efi) nedostupný (ESP nelze připojit)
[ ] Microsoft UEFI CA 2023 (volitelný)
[ ] Option ROM UEFI CA 2023 (volitelný)
REGISTRY:
AvailableUpdates : 0x5944 (naplánováno — start)
UEFICA2023Status : NotStarted
UEFICA2023Error : (žádná)
WindowsUEFICA2023Capable : 1 = cert v DB (boot mgr zatím ne)
UDÁLOSTI (System log — Secure Boot):
2026-06-05 18:42 EventID 1801 Čeká na podmínky (monitor)
2026-06-05 18:43 EventID 1808 Certifikáty úspěšně aplikovány
```
Pokud hard prerekvizita selže, skript dále nepokračuje a zobrazí přesný důvod.
### Fáze — co je povinné pro HOTOVO
| Fáze | Povinná | Splněno když |
|---|---|---|
| Secure Boot zapnutý | ✅ | `Confirm-SecureBootUEFI` = True |
| Servicing task k dispozici | ✅ | existuje `\Microsoft\Windows\PI\Secure-Boot-Update` |
| Servicing task k dispozici | ✅ | task `Secure-Boot-Update` existuje |
| KEK: Microsoft Corporation KEK 2K CA 2023 | ✅ | cert v KEK databázi |
| DB: Windows UEFI CA 2023 | ✅ | cert v DB databázi |
| Boot Manager aktivní (2023) | ✅ | `WindowsUEFICA2023Capable=2` / Event 1808 / 1799 |
| DB: Microsoft UEFI CA 2023 (3rd-party) | | volitelné (pro option ROM / non-Windows boot) |
| AvailableUpdates = 0x0 nebo 0x4000 | ✅ | servisování dokončeno |
| UEFICA2023Status = Updated | | registry klíč |
| Boot Manager ověřen (2023 CA) | ✅ | Capable=2 nebo certutil potvrdil podpis souboru |
| DB: Microsoft UEFI CA 2023 (3rd-party) | — | volitelné (option ROM / non-Windows boot) |
| DB: Option ROM UEFI CA 2023 | — | volitelné |
| DBX: revokace starého boot manageru 2011 | — | volitelné (finální hardening) |
V checklistu: `[✓]` splněno (zeleně), `[ ]` zbývá, `[✗]` chyba/blokováno. Zvýrazní se aktuální krok („← vyžaduje RESTART").
### Detekce — odkud čte
- **Prostředí a HW** (WMI/CIM): fyzický / Hyper-V VM / VMware VM, výrobce, model, BIOS verze a datum
- **Secure Boot stav** a **operating mode** (User / Deployed / **Setup** / Audit) ze standardních UEFI proměnných
- **Certifikáty** v KEK / DB / DBX — primárně parsováním `EFI_SIGNATURE_LIST` na X.509 (subject, thumbprint, platnost), s ASCII fallbackem
- **Boot Manager** `bootmgfw.efi` na ESP — read-only mount + `certutil -dump` (staged podpis); aktivní stav z registru/eventů
- **Certifikáty** v KEK / DB / DBX — parsováním `EFI_SIGNATURE_LIST` na X.509 (subject, thumbprint, platnost), s ASCII fallbackem
- **Boot Manager** `bootmgfw.efi` na ESP:
- Pokus 1: prohledá AZ pro ESP s existujícím písmenem jednotky
- Pokus 2: připojí ESP přes `mountvol <X>: /S` (výchozí S:, lze změnit `-EspDriveLetter`), zkopíruje soubor do `%TEMP%\SecureBootCA2023\` a ověří podpis přes `certutil -dump` — přečte embedded PKCS#7 blob přímo z PE souboru, bez závislosti na local cert store; záloha Event 1799
- **Registry** `HKLM\SYSTEM\CurrentControlSet\Control\SecureBoot[\Servicing]`:
`AvailableUpdates`, `MicrosoftUpdateManagedOptIn`, `HighConfidenceOptOut`, `UEFICA2023Status` (REG_SZ: NotStarted/InProgress/**Updated**), `UEFICA2023Error`, `UEFICA2023ErrorEvent`, `WindowsUEFICA2023Capable` (0/1/**2**), `ConfidenceLevel`
`AvailableUpdates`, `MicrosoftUpdateManagedOptIn`, `HighConfidenceOptOut`, `UEFICA2023Status` (NotStarted / InProgress / **Updated** / Failed), `UEFICA2023Error`, `UEFICA2023ErrorEvent`, `WindowsUEFICA2023Capable` (0 / 1 / **2**), `ConfidenceLevel`
- **BitLocker** systémové jednotky: stav ochrany a typ protektoru (zvýraznění TPM/PCR7 rizika)
- **Event Log** (System, zdroj TPM-WMI): 1795, 1796, 1799, 1800, 1801, 1802, 1803, 1808
@@ -121,41 +161,45 @@ V checklistu: `[✓]` splněno (zeleně), `[ ]` zbývá, `[✗]` chyba/blokován
| Kategorie | Význam | Akce |
|---|---|---|
| **OK** | kompletní 2023 sada, aktivní Boot Manager, 2011 odstraněny | žádná |
| **OK_TRANSITION** | 2023 i aktivní Boot Manager hotové, 2011 ještě přítomné (normální) | žádná, monitorovat |
| **UPDATE_NEEDED** | nic 2023 nenasazeno | zahájit remediaci |
| **UPDATE_PARTIAL** | část nasazena, chybí KEK/DB | pokračovat v cyklu |
| **UPDATE_PENDING** | KEK i DB hotové, zbývá aktivovat Boot Manager | **RESTART**, pak znovu zkontrolovat |
| **OK** | 4 podmínky splněny + Boot Manager ověřen | žádná |
| **OK_TRANSITION** | HOTOVO, ale staré 2011 certy ještě přítomné (normální přechodný stav) | žádná, monitorovat |
| **UPDATE_NEEDED** | Žádné 2023 certy nenasazeny | zahájit remediaci |
| **UPDATE_PARTIAL** | Část certifikátů nasazena, servisování probíhá | spustit task, restart |
| **UPDATE_PENDING_RESTART** | Boot Manager 2023 staged (`AU=0x4100`) — čeká na restart | **RESTART** |
| **UPDATE_BOOTMANAGER** | Certy v DB/KEK OK, ale bootloader dosud nepoužívá 2023 CA | spustit task, restart |
| **UPDATE_FAILED** | Event 1795 / `UEFICA2023ErrorEvent` / status Failed | firmware/OEM, troubleshooting |
| **FIRMWARE_UPDATE_NEEDED** | ConfidenceLevel Temporarily Paused" / Event 1802 | update firmwaru u OEM |
| **NOT_SUPPORTED** | ConfidenceLevel Not Supported" | dokumentovat jako výjimku |
| **FIRMWARE_UPDATE_NEEDED** | `ConfidenceLevel: Temporarily Paused` / Event 1802 | update firmwaru u OEM |
| **NOT_SUPPORTED** | `ConfidenceLevel: Not Supported` | dokumentovat jako výjimku |
| **BUILD_OUTDATED** | Certy přítomny, ale chybí servicing infrastruktura | Windows Update (KB5066835+) |
| **TASK_MISSING** | Chybí servicing úloha | nainstalovat KB5066835+ |
| **SETUP_MODE** | Secure Boot v Setup Mode (chybí enrolled PK) | obnovit klíče v UEFI |
| **TASK_MISSING** | chybí servicing úloha (starý build) | nainstalovat KB5066835+ |
| **SECUREBOOT_DISABLED** | Secure Boot vypnutý | rozhodnout o zapnutí |
| **NO_SECUREBOOT / _VM** | Legacy BIOS / VM bez vTPM | výjimka |
| **SECUREBOOT_DISABLED** | Secure Boot vypnutý | zapnout v UEFI/BIOS |
| **NO_SECUREBOOT** | Legacy BIOS | dokumentovat jako výjimku |
| **NO_SECUREBOOT_VM** | VM bez UEFI nebo vTPM | dokumentovat jako výjimku |
### Remediace (jen po souhlasu, sekvenčně)
1. **Registry** — nastaví `MicrosoftUpdateManagedOptIn=1`, `AvailableUpdates=0x5944`, `HighConfidenceOptOut=0`; zápis se ověří zpětným čtením, než pokračuje dál.
2. **Servicing úloha** — spustí `\Microsoft\Windows\PI\Secure-Boot-Update` a **čeká na změnu** `AvailableUpdates` (nebo na doběh úlohy, timeout 180 s).
3. **Ověření** — znovu proběhne detekce a ukáže aktualizovaný stav fází.
1. **Registry** — nastaví `MicrosoftUpdateManagedOptIn=1`, `AvailableUpdates=0x5944` (jen při první inicializaci nebo po dokončení), `HighConfidenceOptOut=0`; zápis se ověří zpětným čtením.
2. **Servicing úloha** — spustí `\Microsoft\Windows\PI\Secure-Boot-Update` a čeká na změnu `AvailableUpdates` (timeout 180 s).
3. **Ověření** — znovu proběhne detekce a zobrazí aktualizovaný stav.
Skript pak vypíše další kroky (restart, BitLocker, opakování)**bez restartu**.
Po remediaci skript jasně řekne, kdy je restart nutný a upozorní na BitLocker**bez restartu**.
### Stav napříč restarty
### Stav v log souboru
Průběh se ukládá do `%ProgramData%\SecureBootCA2023\state.json` (číslo cyklu, kategorie, hodnota). S `-RegisterResume` se přes `RunOnce` po dalším restartu sama spustí `-CheckOnly` (žádné auto-aplikování, žádný auto-restart).
Průběh se loguje do `%ProgramData%\SecureBootCA2023\SecureBootRemediation.log` a stav cyklu do `%ProgramData%\SecureBootCA2023\state.json`.
### Výstup
### Výstup pro automatizaci (`-CheckOnly` exit kódy)
- Stručný **barevný** souhrn: server, checklist, registry/firmware, verdikt
- Český, faktický, nezahlcuje; důležité informace jsou zvýrazněné
- Volitelný **log** (cesta se vypíše), `-Detailed` rozpis, `-PassThru` objekt
- `-CheckOnly` nastavuje **exit kód** (0/1/2) pro automatizaci
| Exit kód | Význam |
|---|---|
| `0` | HOTOVO — OK nebo OK_TRANSITION |
| `1` | Nutná akce — UPDATE_* kategorie |
| `2` | Blokováno — prerekvizita selhala, NO_SECUREBOOT, SETUP_MODE, apod. |
### Kódování
Skript je uložen jako **UTF-8 s BOM** a na startu vynutí UTF-8 výstup konzole, aby čeština i symboly (`✓`) fungovaly i ve Windows PowerShell 5.1. Symboly checklistu jsou na jednom místě nahoře ve skriptu (`$SYM_DONE` …), kdyby je bylo potřeba změnit.
Skript je uložen jako **UTF-8 s BOM** a na startu vynutí UTF-8 výstup konzole, aby čeština i symboly fungovaly i ve Windows PowerShell 5.1.
---
@@ -166,13 +210,11 @@ Skript je uložen jako **UTF-8 s BOM** a na startu vynutí UTF-8 výstup konzole
| Téma | Odkaz |
|---|---|
| Přehled: expirace certifikátů a CA aktualizace (KB5062710) | [support.microsoft.com](https://support.microsoft.com/en-us/topic/windows-secure-boot-certificate-expiration-and-ca-updates-7ff40d33-95dc-4c3c-8725-a9b95457578e) |
| Guidance for IT Professionals and Organizations | [support.microsoft.com](https://support.microsoft.com/en-us/topic/secure-boot-certificate-updates-guidance-for-it-professionals-and-organizations-e2b43f9f-b424-42df-bc6a-8476db65ab2f) |
| Registry key updates for Secure Boot (IT-managed) | [support.microsoft.com](https://support.microsoft.com/en-us/topic/registry-key-updates-for-secure-boot-windows-devices-with-it-managed-updates-a7be69c9-4634-42e1-9ca1-df06f43f360d) |
| Secure Boot DB and DBX variable update events | [support.microsoft.com](https://support.microsoft.com/en-us/topic/secure-boot-db-and-dbx-variable-update-events-37e47cf8-608b-4a87-8175-bdead630eb69) |
| IT Pro guidance (KB5062713) | [support.microsoft.com](https://support.microsoft.com/en-us/help/5062713) |
| Registry key metoda (KB5068202) | [support.microsoft.com](https://support.microsoft.com/en-us/help/5068202) |
| Guidance for IT Professionals and Organizations (KB5062713) | [support.microsoft.com](https://support.microsoft.com/en-us/help/5062713) |
| Registry key updates for Secure Boot IT-managed (KB5068202) | [support.microsoft.com](https://support.microsoft.com/en-us/help/5068202) |
| GPO metoda (KB5068198) | [support.microsoft.com](https://support.microsoft.com/en-us/help/5068198) |
| WinCS API (KB5068197) | [support.microsoft.com](https://support.microsoft.com/en-us/help/5068197) |
| Secure Boot DB and DBX variable update events | [support.microsoft.com](https://support.microsoft.com/en-us/topic/secure-boot-db-and-dbx-variable-update-events-37e47cf8-608b-4a87-8175-bdead630eb69) |
| Troubleshooting (KB5085046) | [support.microsoft.com](https://support.microsoft.com/en-us/help/5085046) |
| Known issues a resolutions, vč. Hyper-V fix (KB5085790) | [support.microsoft.com](https://support.microsoft.com/en-us/help/5085790) |
| Minimální build 14. 10. 2025 (KB5066835) | [support.microsoft.com](https://support.microsoft.com/en-us/topic/october-14-2025-kb5066835-os-builds-26200-6899-and-26100-6899-1db237d8-9f3b-4218-9515-3e0a32729685) |
@@ -183,8 +225,8 @@ Skript je uložen jako **UTF-8 s BOM** a na startu vynutí UTF-8 výstup konzole
| Projekt | Odkaz |
|---|---|
| CheckCA2023 (claude-boucher) — GUI monitor, podrobná registry/bit reference | [github.com/claude-boucher/CheckCA2023](https://github.com/claude-boucher/CheckCA2023) |
| SecureBoot-CA2023-Automatic-Update (mathisokle) — automatizace přes restarty | [github.com/mathisokle/SecureBoot-CA2023-Automatic-Update](https://github.com/mathisokle/SecureBoot-CA2023-Automatic-Update) |
| SecureBoot-CA2023-Automatic-Update (mathisokle) — automatizace, ESP mount logika | [github.com/mathisokle/SecureBoot-CA2023-Automatic-Update](https://github.com/mathisokle/SecureBoot-CA2023-Automatic-Update) |
---
*Skript záměrně NErestartuje server. Implementuje metodu registry klíčů (KB5068202); metody GPO / Intune / WinCS nepokrývá.*
*Skript záměrně NErestartuje server (výjimka: `-AutoRestart`). Implementuje metodu registry klíčů (KB5068202); metody GPO / Intune / WinCS nepokrývá.*
-519
View File
@@ -1,519 +0,0 @@
#Requires -Version 5.1
<#
.SYNOPSIS
Remediační skript pro aktualizaci Secure Boot certifikátů (KB5068202).
.DESCRIPTION
Nastaví registry klíče pro zahájení aktualizace Secure Boot certifikátů
(přechod z 2011 na 2023 sadu) a spustí scheduled task.
Metoda: Registry key (KB5068202)
Alternativy: GPO (KB5068198), WinCS CLI (KB5068197)
Postup remediace:
1. Skript nastaví AvailableUpdates = 0x5944 v registry
2. Scheduled task \Microsoft\Windows\PI\Secure-Boot-Update se spustí
(nebo čeká na příští plánovaný běh každých 12 hodin)
3. Certifikáty se aplikují při příštím restartu
4. Výsledek ověřit přes EventID 1808 (úspěch) nebo 1801/1795 (chyba)
DŮLEŽITÉ:
- Aktualizace vyžaduje RESTART pro aplikaci certifikátů v Boot Manageru
- Neúspěšná aktualizace NEOHROZÍ boot serveru — server běží dál
- Po aplikaci čekat min. 48 hodin a jeden nebo více restartů
- Na Hyper-V VM s Event 1795: ověřit verzi hostitele (fix dostupný od 3/2026)
.PARAMETER ServerName
Název nebo pole názvů serverů pro vzdálené spuštění přes WinRM.
Pokud není zadán, skript se spustí lokálně.
.PARAMETER Credential
Přihlašovací údaje pro vzdálené připojení.
.PARAMETER Force
Aplikuje registry klíče i na serverech, které již mají 2023 certifikáty
nebo kde již aktualizace proběhla (UEFICA2023Status = 2).
.PARAMETER SkipScheduledTask
Nespustí scheduled task po nastavení registry. Task se spustí sám
při příštím plánovaném běhu (každých 12 hodin).
.PARAMETER VerifyOnly
Pouze zobrazí aktuální stav bez provedení změn (read-only, ekvivalent -WhatIf).
.PARAMETER LogPath
Cesta k logovacímu souboru. Default: .\SecureBootRemediation-<datum>.log
.EXAMPLE
# WhatIf — zobrazí co by se stalo, bez změn
.\Set-SecureBootCertificateUpdate.ps1 -WhatIf
.EXAMPLE
# Lokální spuštění
.\Set-SecureBootCertificateUpdate.ps1
.EXAMPLE
# Vzdálené spuštění na jednom serveru
.\Set-SecureBootCertificateUpdate.ps1 -ServerName SERVER01
.EXAMPLE
# Vzdálené spuštění na více serverech
.\Set-SecureBootCertificateUpdate.ps1 -ServerName SERVER01,SERVER02,SERVER03
.EXAMPLE
# Pouze ověření bez změn
.\Set-SecureBootCertificateUpdate.ps1 -ServerName SERVER01 -VerifyOnly
.NOTES
Reference: KB5062710, KB5068202, KB5085046, KB5085790
Vyžaduje spuštění jako Administrator (lokálně i vzdáleně).
Vzdálené spuštění vyžaduje WinRM (standardně aktivní na Windows Server).
AvailableUpdates = 0x5944 dle KB5068202 (KEK + UEFI CA + Windows UEFI CA + Boot Manager).
#>
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]
param(
[Parameter(Position = 0)]
[string[]]$ServerName,
[System.Management.Automation.PSCredential]$Credential,
[switch]$Force,
[switch]$SkipScheduledTask,
[switch]$VerifyOnly,
[string]$LogPath = ".\SecureBootRemediation-$(Get-Date -Format 'yyyyMMdd-HHmmss').log"
)
$ErrorActionPreference = 'SilentlyContinue'
Set-StrictMode -Off
#region ── Logging ────────────────────────────────────────────────────────────
function Write-Log {
param(
[string]$Message,
[ValidateSet('INFO','WARN','ERROR','SUCCESS','ACTION')]
[string]$Level = 'INFO'
)
$ts = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
$line = "[$ts] [$Level] $Message"
$color = switch ($Level) {
'SUCCESS' { 'Green' }
'WARN' { 'Yellow' }
'ERROR' { 'Red' }
'ACTION' { 'Cyan' }
default { 'Gray' }
}
Write-Host $line -ForegroundColor $color
try {
$line | Out-File -FilePath $LogPath -Append -Encoding UTF8 -Force
} catch { }
}
#endregion
#region ── Core remediation logic (spouští se lokálně nebo přes Invoke-Command) ──
$RemediationScriptBlock = {
param(
[bool]$WhatIfMode,
[bool]$ForceMode,
[bool]$VerifyOnlyMode,
[bool]$SkipTask
)
$result = [ordered]@{
Hostname = $env:COMPUTERNAME
Timestamp = (Get-Date).ToString('yyyy-MM-ddTHH:mm:ssZ')
PreState = $null
ActionsApplied = @()
PostState = $null
Status = 'Unknown'
Message = ''
RequiresRestart = $false
}
# Registry konstanty (KB5068202)
$REG_SECUREBOOT = 'HKLM:\SYSTEM\CurrentControlSet\Control\SecureBoot'
$REG_SERVICING = 'HKLM:\SYSTEM\CurrentControlSet\Control\SecureBoot\Servicing'
$AVAILABLE_UPDATES_VALUE = 0x5944 # KEK + UEFI CA + Windows UEFI CA + Boot Manager (KB5068202)
$TASK_PATH = '\Microsoft\Windows\PI\Secure-Boot-Update'
#-- Funkce: zjistit aktuální stav -----------------------------------------
function Get-State {
$s = [ordered]@{
FirmwareType = 'Unknown'
SecureBootEnabled = $false
SecureBootSupported = $false
AvailableUpdates = $null
HighConfidenceOptOut = $null
UEFICA2023Status = $null
UEFICA2023StatusText = 'Unknown'
UEFICA2023Error = $null
WindowsUEFICA2023Capable = $null
LastRelevantEvent = $null
LastRelevantEventTime = $null
}
# Firmware type
$fw = Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control' -Name PEFirmwareType -ErrorAction SilentlyContinue
if ($fw) {
$s.FirmwareType = if ($fw.PEFirmwareType -eq 2) { 'UEFI' } else { 'Legacy BIOS' }
} elseif (Test-Path $REG_SECUREBOOT) {
$s.FirmwareType = 'UEFI'
} else {
$s.FirmwareType = 'Legacy BIOS'
}
# Secure Boot stav
try {
$r = Confirm-SecureBootUEFI -ErrorAction Stop
$s.SecureBootSupported = $true
$s.SecureBootEnabled = [bool]$r
} catch { }
# Registry hodnoty
$mainProps = Get-ItemProperty $REG_SECUREBOOT -ErrorAction SilentlyContinue
if ($mainProps) {
$s.AvailableUpdates = $mainProps.AvailableUpdates
$s.HighConfidenceOptOut = $mainProps.HighConfidenceOptOut
}
$svcProps = Get-ItemProperty $REG_SERVICING -ErrorAction SilentlyContinue
if ($svcProps) {
$s.UEFICA2023Status = $svcProps.UEFICA2023Status
$s.UEFICA2023Error = $svcProps.UEFICA2023Error
$s.WindowsUEFICA2023Capable = $svcProps.WindowsUEFICA2023Capable
$s.UEFICA2023StatusText = switch ($s.UEFICA2023Status) {
0 { 'NotStarted' }
1 { 'InProgress' }
2 { 'Success' }
3 { 'Failed' }
$null { 'KeyNotPresent' }
default { "Unknown ($($s.UEFICA2023Status))" }
}
}
# Poslední relevantní event
try {
$evt = Get-WinEvent -FilterHashtable @{
LogName = 'System'
Id = @(1795,1801,1808)
} -MaxEvents 1 -ErrorAction Stop | Select-Object -First 1
if ($evt) {
$s.LastRelevantEvent = $evt.Id
$s.LastRelevantEventTime = $evt.TimeCreated.ToString('yyyy-MM-dd HH:mm:ss')
}
} catch { }
return $s
}
#-- Představ aktuální stav ------------------------------------------------
$result.PreState = Get-State
$pre = $result.PreState
# Abort podmínky (bez -Force)
if (-not $VerifyOnlyMode -and -not $WhatIfMode) {
if ($pre.FirmwareType -ne 'UEFI') {
$result.Status = 'Skipped'
$result.Message = 'Legacy BIOS — Secure Boot není podporováno, remediace není možná.'
return $result
}
if (-not $pre.SecureBootSupported) {
$result.Status = 'Skipped'
$result.Message = 'Secure Boot není podporováno nebo povoleno — remediace přeskočena.'
return $result
}
if ($pre.UEFICA2023Status -eq 2 -and -not $ForceMode) {
$result.Status = 'Skipped'
$result.Message = "Aktualizace již byla úspěšně dokončena (UEFICA2023Status=2). Použijte -Force pro opakované spuštění."
return $result
}
if ($pre.WindowsUEFICA2023Capable -eq 0 -and -not $ForceMode) {
$result.Status = 'Warning'
$result.Message = "WindowsUEFICA2023Capable=0 — firmware nemusí podporovat nové certifikáty. Ověřte firmware u výrobce. Použijte -Force pro vynucení."
return $result
}
if ($pre.LastRelevantEvent -eq 1795) {
$result.Status = 'Warning'
$result.Message = "Detekován Event 1795 (Hyper-V known issue). Ověřte verzi hostitele — fix je dostupný od 3/2026 (KB5085790). Použijte -Force pro pokračování."
if (-not $ForceMode) { return $result }
}
}
if ($VerifyOnlyMode) {
$result.Status = 'VerifyOnly'
$result.Message = 'VerifyOnly mode — žádné změny neprovedeny.'
return $result
}
if ($WhatIfMode) {
$result.ActionsApplied += "WhatIf: Nastavil by HKLM:\...\SecureBoot\AvailableUpdates = 0x$($AVAILABLE_UPDATES_VALUE.ToString('X4'))"
$result.ActionsApplied += "WhatIf: Zajistil by HighConfidenceOptOut = 0"
if (-not $SkipTask) {
$result.ActionsApplied += "WhatIf: Spustil by scheduled task '$TASK_PATH'"
}
$result.Status = 'WhatIf'
$result.Message = 'WhatIf mode — žádné změny neprovedeny.'
return $result
}
#-- Aplikovat registry klíče (KB5068202) ----------------------------------
# 1) Zajistit existenci SecureBoot klíče (obvykle existuje)
if (-not (Test-Path $REG_SECUREBOOT)) {
try {
New-Item -Path $REG_SECUREBOOT -Force -ErrorAction Stop | Out-Null
$result.ActionsApplied += "Vytvořen registry klíč: $REG_SECUREBOOT"
} catch {
$result.Status = 'Error'
$result.Message = "Nelze vytvořit registry klíč $REG_SECUREBOOT : $($_.Exception.Message)"
return $result
}
}
# 2) Nastavit AvailableUpdates = 0x5944
try {
Set-ItemProperty -Path $REG_SECUREBOOT -Name 'AvailableUpdates' `
-Value $AVAILABLE_UPDATES_VALUE -Type DWord -Force -ErrorAction Stop
$result.ActionsApplied += "Nastaveno AvailableUpdates = 0x$($AVAILABLE_UPDATES_VALUE.ToString('X4')) (KB5068202)"
} catch {
$result.Status = 'Error'
$result.Message = "Chyba při nastavení AvailableUpdates: $($_.Exception.Message)"
return $result
}
# 3) HighConfidenceOptOut musí být 0 (nebo neexistovat) — jinak se update neprovede
$optOut = (Get-ItemProperty $REG_SECUREBOOT -Name 'HighConfidenceOptOut' -ErrorAction SilentlyContinue)
if ($optOut -and $optOut.HighConfidenceOptOut -ne 0) {
try {
Set-ItemProperty -Path $REG_SECUREBOOT -Name 'HighConfidenceOptOut' `
-Value 0 -Type DWord -Force -ErrorAction Stop
$result.ActionsApplied += "Resetován HighConfidenceOptOut = 0 (byl $($optOut.HighConfidenceOptOut))"
} catch {
$result.ActionsApplied += "WARN: Nepodařilo se resetovat HighConfidenceOptOut: $($_.Exception.Message)"
}
}
# 4) Zajistit existenci Servicing podklíče
if (-not (Test-Path $REG_SERVICING)) {
try {
New-Item -Path $REG_SERVICING -Force -ErrorAction Stop | Out-Null
$result.ActionsApplied += "Vytvořen podklíč: $REG_SERVICING"
} catch { }
}
#-- Spustit scheduled task ------------------------------------------------
if (-not $SkipTask) {
try {
$task = Get-ScheduledTask -TaskPath '\Microsoft\Windows\PI\' `
-TaskName 'Secure-Boot-Update' -ErrorAction Stop
if ($task) {
Start-ScheduledTask -TaskPath '\Microsoft\Windows\PI\' `
-TaskName 'Secure-Boot-Update' -ErrorAction Stop
$result.ActionsApplied += "Spuštěn scheduled task: $TASK_PATH"
# Čekat na dokončení tasku (timeout 120 sekund)
$timeoutSec = 120
$intervalSec = 3
$elapsed = 0
$finalState = 'Unknown'
do {
Start-Sleep -Seconds $intervalSec
$elapsed += $intervalSec
$finalState = (Get-ScheduledTask -TaskPath '\Microsoft\Windows\PI\' `
-TaskName 'Secure-Boot-Update' `
-ErrorAction SilentlyContinue).State
} while ($finalState -eq 'Running' -and $elapsed -lt $timeoutSec)
if ($elapsed -ge $timeoutSec -and $finalState -eq 'Running') {
$result.ActionsApplied += "WARN: Task stále běží po $timeoutSec s — pokračuji bez čekání. Ověřit stav ručně."
} else {
$result.ActionsApplied += "Task dokončen za ${elapsed}s — stav: $finalState"
}
}
} catch {
$result.ActionsApplied += "INFO: Scheduled task '$TASK_PATH' nebyl nalezen nebo se nepodařilo spustit: $($_.Exception.Message)"
$result.ActionsApplied += "INFO: Task se spustí automaticky při příštím plánovaném běhu (každých 12 hodin)."
}
} else {
$result.ActionsApplied += "INFO: Spuštění scheduled task přeskočeno (-SkipScheduledTask). Task se spustí sám (každých 12 hodin)."
}
#-- Post-stav (okamžitě po aplikaci, před restartem) ---------------------
Start-Sleep -Seconds 2
$result.PostState = Get-State
$result.RequiresRestart = $true
$result.Status = 'Applied'
$result.Message = "Registry klíče nastaveny. Server vyžaduje RESTART pro aplikaci certifikátů. Po restartu ověřit EventID 1808 v System logu."
return $result
}
#endregion
#region ── Výstup výsledku ────────────────────────────────────────────────────
function Write-ResultSummary {
param($res)
$line = '-' * 60
Write-Log $line
Write-Log "SERVER: $($res.Hostname) | $($res.Timestamp)"
$_statusLevel = switch ($res.Status) {
'Applied' { 'SUCCESS' }
'Error' { 'ERROR' }
'Warning' { 'WARN' }
'Skipped' { 'WARN' }
default { 'INFO' }
}
Write-Log "STATUS: $($res.Status)$($res.Message)" -Level $_statusLevel
$pre = $res.PreState
if ($pre) {
Write-Log " Pre-state:"
Write-Log " Firmware : $($pre.FirmwareType)"
Write-Log " Secure Boot : podporováno=$($pre.SecureBootSupported), povoleno=$($pre.SecureBootEnabled)"
Write-Log " AvailableUpdates : $($pre.AvailableUpdates)"
Write-Log " UEFICA2023Status : $($pre.UEFICA2023Status) ($($pre.UEFICA2023StatusText))"
Write-Log " WindowsCapable : $($pre.WindowsUEFICA2023Capable)"
if ($pre.UEFICA2023Error) {
Write-Log " ! UEFICA2023Error : $($pre.UEFICA2023Error)" -Level 'WARN'
}
if ($pre.LastRelevantEvent) {
Write-Log " Poslední event : EventID $($pre.LastRelevantEvent) ($($pre.LastRelevantEventTime))"
}
}
if ($res.ActionsApplied -and $res.ActionsApplied.Count -gt 0) {
Write-Log " Provedené akce:"
foreach ($action in $res.ActionsApplied) {
$lvl = if ($action -like 'WhatIf:*' -or $action -like 'INFO:*') { 'INFO' }
elseif ($action -like 'WARN:*') { 'WARN' }
else { 'ACTION' }
Write-Log " $action" -Level $lvl
}
}
$post = $res.PostState
if ($post -and $res.Status -eq 'Applied') {
Write-Log " Post-state (před restartem):"
Write-Log " AvailableUpdates : $($post.AvailableUpdates)"
Write-Log " UEFICA2023Status : $($post.UEFICA2023Status) ($($post.UEFICA2023StatusText))"
}
if ($res.RequiresRestart) {
Write-Log " *** VYŽADOVÁN RESTART pro aplikaci certifikátů ***" -Level 'WARN'
Write-Log " Po restartu ověřit: Get-WinEvent -FilterHashtable @{LogName='System';Id=1808} -MaxEvents 5"
}
}
#endregion
#region ── Main ───────────────────────────────────────────────────────────────
$isWhatIf = $WhatIfPreference -eq [Management.Automation.ActionPreference]::Continue
$isVerify = $VerifyOnly.IsPresent
$isForce = $Force.IsPresent
$isSkipTask = $SkipScheduledTask.IsPresent
Write-Log ('=' * 60)
Write-Log "Set-SecureBootCertificateUpdate — START"
Write-Log "Parametry: WhatIf=$isWhatIf, Force=$isForce, VerifyOnly=$isVerify, SkipTask=$isSkipTask"
Write-Log "Log: $LogPath"
Write-Log ('=' * 60)
$results = @()
if (-not $ServerName -or $ServerName.Count -eq 0) {
# ── Lokální spuštění ──
Write-Log "Spouštím lokálně na: $env:COMPUTERNAME" -Level 'INFO'
if (-not $isWhatIf -and -not $isVerify) {
if (-not $PSCmdlet.ShouldProcess($env:COMPUTERNAME, "Nastavit Secure Boot registry klíče (AvailableUpdates=0x5944)")) {
Write-Log "Uživatel zamítl akci — konec." -Level 'WARN'
exit 0
}
}
$res = & $RemediationScriptBlock -WhatIfMode $isWhatIf -ForceMode $isForce `
-VerifyOnlyMode $isVerify -SkipTask $isSkipTask
Write-ResultSummary $res
$results += $res
} else {
# ── Vzdálené spuštění ──
Write-Log "Cílové servery ($($ServerName.Count)): $($ServerName -join ', ')" -Level 'INFO'
$invokeParams = @{
ComputerName = $ServerName
ScriptBlock = $RemediationScriptBlock
ArgumentList = @($isWhatIf, $isForce, $isVerify, $isSkipTask)
ErrorAction = 'SilentlyContinue'
ThrottleLimit = 10
}
if ($Credential) { $invokeParams['Credential'] = $Credential }
$remoteResults = Invoke-Command @invokeParams
foreach ($res in $remoteResults) {
Write-ResultSummary $res
$results += $res
}
# Servery, které neodpověděly
$respondedHosts = $remoteResults | ForEach-Object { $_.Hostname }
foreach ($srv in $ServerName) {
if ($srv -notin $respondedHosts) {
Write-Log " ! Server '$srv' neodpověděl (WinRM nedostupný nebo přihlášení selhalo)" -Level 'ERROR'
$results += [ordered]@{
Hostname = $srv; Status = 'Unreachable'; Message = 'WinRM nedostupný'
PreState = $null; PostState = $null; ActionsApplied = @(); RequiresRestart = $false
}
}
}
}
#-- Souhrn ------------------------------------------------------------------
Write-Log ('=' * 60)
Write-Log "SOUHRN REMEDIACE"
$grouped = $results | Group-Object Status
foreach ($g in $grouped) {
$lvl = switch ($g.Name) {
'Applied' { 'SUCCESS' }
'Error' { 'ERROR' }
'Unreachable' { 'ERROR' }
'Skipped' { 'WARN' }
'Warning' { 'WARN' }
default { 'INFO' }
}
Write-Log " $($g.Name): $($g.Count) server(ů) [$(($g.Group.Hostname) -join ', ')]" -Level $lvl
}
$needRestart = @($results | Where-Object { $_.RequiresRestart })
if ($needRestart.Count -gt 0) {
Write-Log ''
Write-Log "VYŽADOVÁN RESTART ($($needRestart.Count) server(ů)): $($needRestart.Hostname -join ', ')" -Level 'WARN'
Write-Log "Po restartu ověřit EventID 1808 v System logu nebo spustit:"
Write-Log " Invoke-Command -ComputerName <server> -ScriptBlock { Get-WinEvent -FilterHashtable @{LogName='System';Id=@(1801,1808)} -MaxEvents 5 | Select TimeCreated,Id,Message }" -Level 'INFO'
}
Write-Log ('=' * 60)
Write-Log "Set-SecureBootCertificateUpdate — KONEC | Log: $LogPath"
Write-Log ('=' * 60)
# Vrátit výsledky jako objekty pro pipeline
$results
#endregion