#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 } # ── 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 $status.AllReplacements2023 = $status.KEK.Has2023 -and $status.DB.Has2023UEFI -and $status.DB.Has2023OptionROM -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 if ($cert.AllReplacements2023 -and -not $cert.AnyExpiring2011) { return @{ Code = 'OK'; Emoji = '✅'; Label = 'OK — má nové 2023 certifikáty' } } if ($cert.AllReplacements2023 -and $cert.AnyExpiring2011) { return @{ Code = 'OK_TRANSITION'; Emoji = '✅'; Label = 'OK — přechodný stav (2023 i 2011 certifikáty)' } } # 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 } } # 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