Initial commit: detekční skript, remediační skript a README
- Invoke-SecureBootAudit.ps1: audit Secure Boot certifikátů (Fáze 1) - detekce prostředí (Physical/Hyper-V VM/VMware VM) - parsování EFI_SIGNATURE_LIST, detekce 2011/2023 certifikátů - registry stav, Event Log analýza, kategorizace, JSON výstup - Set-SecureBootCertificateUpdate.ps1: remediace dle KB5068202 - AvailableUpdates = 0x5944 - scheduled task s wait smyčkou (timeout 120s) - WhatIf, Force, VerifyOnly, WinRM - README.md: popis problému, detekce stavů, remediační postup
This commit is contained in:
@@ -0,0 +1,519 @@
|
||||
#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
|
||||
Reference in New Issue
Block a user