Copie e cole este script de exemplo e modifique conforme necessário para o seu ambiente:
<# . SYNOPSIS Deteta status de atualização de certificados de Arranque Seguro para monitorização em toda a frota.
.DESCRIPTION Este script de deteção recolhe status de Arranque Seguro, valores de registo de atualização de certificados, e informações do dispositivo. Produz uma cadeia JSON para monitorização e relatórios.
Compatible with Intune Remediations, GPO-based collection, and other management tools. Não é necessário nenhum script de remediação — isto é apenas monitorização.
Exit 0 = "Without issue" (certificates updated) Exit 1 = "With issue" (certificates not updated — informational only)
.PARAMETER OutputPath Opcional. Caminho para uma pasta onde o ficheiro JSON será guardado.Se for fornecido, guarda HOSTNAME_latest.json nesta pasta.Se não for fornecido, produz JSON para stdout (comportamento original).
.EXAMPLE # Saída para stdout (deteção de Intune/SCCM) .\Detect-SecureBootCertUpdateStatus.ps1
.EXAMPLE # Guardar na partilha de rede (implementação de GPO) .\Detect-SecureBootCertUpdateStatus.ps1 -OutputPath "\\server\SecureBootLogs$"
.NOTES Caminhos de registo por https://aka.ms/securebootplaybook: HKLM:\SYSTEM\CurrentControlSet\Control\SecureBoot HKLM:\SYSTEM\CurrentControlSet\Control\SecureBoot\Servicing
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLÍCITO, INCLUINDO, MAS NÃO SE LIMITANDO ÀS GARANTIAS DE COMERCIALIZAÇÃO, FITNESS PARA UM PROPÓSITO PARTICULAR E NONINFRINGEMENT. EM NENHUM CASO, O AUTORES OU TITULARES DE DIREITOS DE AUTOR SÃO RESPONSÁVEIS POR QUALQUER RECLAMAÇÃO, DANOS OU OUTROS RESPONSABILIDADE, SEJA NUMA AÇÃO DE CONTRATO, TORT OU DE OUTRA FORMA, DECORRENTE DE, FORA OU EM LIGAÇÃO COM O SOFTWARE, A UTILIZAÇÃO OU OUTRAS TRANSACÇÕES NO SOFTWARE.#> parâmetro( [Parameter(Mandatory = $false)] [string]$OutputPath )
# Download URL: https://aka.ms/getsecureboot -> "Deployment and Monitoring Samples" Nota: este script é executado em pontos finais para recolher dados de status de Arranque Seguro.
# 1. HostName # PS Version: All | Administração: Não | Requisitos de Sistema: Nenhum experimente { $hostname = $env:COMPUTERNAME if ([string]::IsNullOrEmpty($hostname)) { Write-Warning "Não foi possível determinar o nome do anfitrião" $hostname = "Desconhecido" } Write-Host "Hostname: $hostname" } captura { Write-Warning "Erro ao obter o nome do anfitrião: $_" $hostname = "Erro" Write-Host "Hostname: $hostname" }
# 2. CollectionTime # PS Version: All | Administração: Não | Requisitos de Sistema: Nenhum experimente { $collectionTime = Get-Date if ($null -eq $collectionTime) { Write-Warning "Não foi possível obter a data/hora atuais" $collectionTime = "Desconhecido" } Write-Host "Tempo da Coleção: $collectionTime" } captura { Write-Warning "Erro ao obter a data/hora: $_" $collectionTime = "Erro" Write-Host "Tempo da Coleção: $collectionTime" }
# Registry: Secure Boot Main Key (3 values)
# 3. SecureBootEnabled # PS Version: 3.0+ | Administração: Pode ser necessário | Requisitos de Sistema: sistema compatível com UEFI/Arranque Seguro experimente { $secureBootEnabled = Confirm-SecureBootUEFI -ErrorAction Stop Write-Host "Arranque Seguro Ativado: $secureBootEnabled" } captura { Write-Warning "Não é possível determinar a status de Arranque Seguro através do cmdlet: $_" # Experimente a contingência do registo experimente { $regValue = Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\SecureBoot\State" -Name UEFISecureBootEnabled -ErrorAction Stop $secureBootEnabled = [bool]$regValue.UEFISecureBootEnabled Write-Host "Arranque Seguro Ativado: $secureBootEnabled" } captura { Write-Warning "Não é possível determinar a status de Arranque Seguro através do registo. O sistema pode não suportar UEFI/Arranque Seguro." $secureBootEnabled = $null Write-Host "Arranque Seguro Ativado: Não Disponível" } }
# 4. HighConfidenceOptOut # PS Version: All | Administração: Pode ser necessário | Requisitos de Sistema: Nenhum experimente { $regValue = Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\SecureBoot" -Name HighConfidenceOptOut -ErrorAction Stop $highConfidenceOptOut = $regValue.HighConfidenceOptOut Write-Host "Opção de Exclusão de Confiança Elevada: $highConfidenceOptOut" } captura { # HighConfidenceOptOut é opcional – não está presente na maioria dos sistemas $highConfidenceOptOut = $null Write-Host "Opção de Elevada Confiança: Não Definida" }
# 4b. MicrosoftUpdateManagedOptIn # PS Version: All | Administração: Pode ser necessário | Requisitos de Sistema: Nenhum experimente { $regValue = Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\SecureBoot" -Name MicrosoftUpdateManagedOptIn -ErrorAction Stop $microsoftUpdateManagedOptIn = $regValue.MicrosoftUpdateManagedOptIn Write-Host "Microsoft Update Managed Opt In: $microsoftUpdateManagedOptIn" } captura { # MicrosoftUpdateManagedOptIn é opcional – não está presente na maioria dos sistemas $microsoftUpdateManagedOptIn = $null Write-Host "Microsoft Update Managed Opt In: Not Set" }
# 5. AvailableUpdates # PS Version: All | Administração: Pode ser necessário | Requisitos de Sistema: Nenhum experimente { $regValue = Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\SecureBoot" -Name AvailableUpdates -ErrorAction Stop $availableUpdates = $regValue.AvailableUpdates if ($null -ne $availableUpdates) { # Converter em formato hexadecimal $availableUpdatesHex = "0x{0:X}" -f $availableUpdates Write-Host "Atualizações Disponível: $availableUpdatesHex" } senão { Write-Host "Disponível Atualizações: Não Disponível" } } captura { Write-Warning "Chave de registo AvailableUpdates não encontrada ou inacessível" $availableUpdates = $null Write-Host "Disponível Atualizações: Não Disponível" }
# 5b. AvailableUpdatesPolicy (GPO-controlled persistent value) # PS Version: All | Administração: Pode ser necessário | Requisitos de Sistema: Nenhum experimente { $regValue = Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\SecureBoot" -Name AvailableUpdatesPolicy -ErrorAction Stop $availableUpdatesPolicy = $regValue.AvailableUpdatesPolicy if ($null -ne $availableUpdatesPolicy) { # Converter em formato hexadecimal $availableUpdatesPolicyHex = "0x{0:X}" -f $availableUpdatesPolicy Write-Host "Política de Atualizações Disponível: $availableUpdatesPolicyHex" } senão { Write-Host "Política de Atualizações Disponível: Não Definida" } } captura { # AvailableUpdatesPolicy é opcional - apenas definido quando o GPO é aplicado $availableUpdatesPolicy = $null Write-Host "Política de Atualizações Disponível: Não Definida" }
# Registry: Servicing Key (3 values)
# 6. UEFICA2023Status # PS Version: All | Administração: Pode ser necessário | Requisitos de Sistema: Nenhum experimente { $regValue = Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\SecureBoot\Servicing" -Name UEFICA2023Status -ErrorAction Stop $uefica 2023Status = $regValue.UEFICA2023Status Write-Host "Estado da AC 2023 do UEFI do Windows: $uefica 2023Status" } captura { Write-Warning "Chave de registo de estado da AC 2023 do UEFI do Windows não encontrada ou inacessível" $uefica 2023Status = $null Write-Host "Estado da AC 2023 do UEFI do Windows: Não Disponível" }
# 7. UEFICA2023Error # PS Version: All | Administração: Pode ser necessário | Requisitos de Sistema: Nenhum experimente { $regValue = Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\SecureBoot\Servicing" -Name UEFICA2023Error -ErrorAction Stop $uefica 2023Error = $regValue.UEFICA2023Error Write-Host "Erro UEFI CA 2023: $uefica 2023Error" } captura { # UEFICA2023Error só existe se tiver ocorrido um erro – a ausência é boa $uefica 2023Error = $null Write-Host "Erro UEFI CA 2023: Nenhum" }
# 8. UEFICA2023ErrorEvent # PS Version: All | Administração: Pode ser necessário | Requisitos de Sistema: Nenhum experimente { $regValue = Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\SecureBoot\Servicing" -Name UEFICA2023ErrorEvent -ErrorAction Stop $uefica 2023ErrorEvent = $regValue.UEFICA2023ErrorEvent Write-Host "Evento de Erro da AC UEFI 2023: $uefica 2023ErrorEvent" } captura { $uefica 2023ErrorEvent = $null Write-Host "Evento de Erro da AC UEFI 2023: Não Disponível" }
# Registry: Device Attributes (7 values: 9-15)
# 9. OEMManufacturerName # PS Version: All | Administração: Pode ser necessário | Requisitos de Sistema: Nenhum experimente { $regValue = Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\SecureBoot\Servicing\DeviceAttributes" -Name OEMManufacturerName -ErrorAction Stop $oemManufacturerName = $regValue.OEMManufacturerName if ([string]::IsNullOrEmpty($oemManufacturerName)) { Write-Warning "OEMManufacturerName está vazio" $oemManufacturerName = "Desconhecido" } Write-Host "Nome do Fabricante OEM: $oemManufacturerName" } captura { Write-Warning "Chave de registo OEMManufacturerName não encontrada ou inacessível" $oemManufacturerName = $null Write-Host "Nome do Fabricante OEM: Não Disponível" }
# 10. OEMModelSystemFamily # PS Version: All | Administração: Pode ser necessário | Requisitos de Sistema: Nenhum experimente { $regValue = Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\SecureBoot\Servicing\DeviceAttributes" -Name OEMModelSystemFamily -ErrorAction Stop $oemModelSystemFamily = $regValue.OEMModelSystemFamily if ([string]::IsNullOrEmpty($oemModelSystemFamily)) { Write-Warning "OEMModelSystemFamily está vazio" $oemModelSystemFamily = "Desconhecido" } Write-Host "OEM Model System Family: $oemModelSystemFamily" } captura { Write-Warning "Chave de registo OEMModelSystemFamily não encontrada ou inacessível" $oemModelSystemFamily = $null Write-Host "Família do Sistema do Modelo OEM: Não Disponível" }
# 11. OEMModelNumber # PS Version: All | Administração: Pode ser necessário | Requisitos de Sistema: Nenhum experimente { $regValue = Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\SecureBoot\Servicing\DeviceAttributes" -Name OEMModelNumber -ErrorAction Stop $oemModelNumber = $regValue.OEMModelNumber if ([string]::IsNullOrEmpty($oemModelNumber)) { Write-Warning "OEMModelNumber está vazio" $oemModelNumber = "Desconhecido" } Write-Host "Número do Modelo OEM: $oemModelNumber" } captura { Write-Warning "Chave de registo OEMModelNumber não encontrada ou inacessível" $oemModelNumber = $null Write-Host "Número do Modelo OEM: Não Disponível" }
# 12. FirmwareVersion # PS Version: All | Administração: Pode ser necessário | Requisitos de Sistema: Nenhum experimente { $regValue = Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\SecureBoot\Servicing\DeviceAttributes" -Name FirmwareVersion -ErrorAction Stop $firmwareVersion = $regValue.FirmwareVersion if ([string]::IsNullOrEmpty($firmwareVersion)) { Write-Warning "FirmwareVersion está vazio" $firmwareVersion = "Desconhecido" } Write-Host "Versão de Firmware: $firmwareVersion" } captura { Write-Warning "FirmwareVersion registry key not found or inaccessible" $firmwareVersion = $null Write-Host "Versão de Firmware: Não Disponível" }
# 13. FirmwareReleaseDate # PS Version: All | Administração: Pode ser necessário | Requisitos de Sistema: Nenhum experimente { $regValue = Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\SecureBoot\Servicing\DeviceAttributes" -Name FirmwareReleaseDate -ErrorAction Stop $firmwareReleaseDate = $regValue.FirmwareReleaseDate if ([string]::IsNullOrEmpty($firmwareReleaseDate)) { Write-Warning "FirmwareReleaseDate está vazio" $firmwareReleaseDate = "Desconhecido" } Write-Host "Data de Lançamento do Firmware: $firmwareReleaseDate" } captura { Write-Warning "FirmwareReleaseDate registry key not found or inaccessible" $firmwareReleaseDate = $null Write-Host "Data de Lançamento do Firmware: Não Disponível" }
# 14. OSArchitecture # PS Version: All | Administração: Não | Requisitos de Sistema: Nenhum experimente { $osArchitecture = $env:PROCESSOR_ARCHITECTURE if ([string]::IsNullOrEmpty($osArchitecture)) { # Experimente a contingência do registo $regValue = Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\SecureBoot\Servicing\DeviceAttributes" -Name OSArchitecture -ErrorAction Stop $osArchitecture = $regValue.OSArchitecture } if ([string]::IsNullOrEmpty($osArchitecture)) { Write-Warning "Não foi possível determinar a OSArchitecture" $osArchitecture = "Desconhecido" } Write-Host "Arquitetura do SO: $osArchitecture" } captura { Write-Warning "Erro ao obter OSArchitecture: $_" $osArchitecture = "Desconhecido" Write-Host "Arquitetura do SO: $osArchitecture" }
# 15. CanAttemptUpdateAfter (FILETIME) # PS Version: All | Administração: Pode ser necessário | Requisitos de Sistema: Nenhum experimente { $regValue = Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\SecureBoot\Servicing\DeviceAttributes" -Name CanAttemptUpdateAfter -ErrorAction Stop $canAttemptUpdateAfter = $regValue.CanAttemptUpdateAfter # Convert FILETIME to UTC DateTime — registry stores as REG_BINARY (byte[]) or REG_QWORD (long) if ($null -ne $canAttemptUpdateAfter) { experimente { if ($canAttemptUpdateAfter -is [byte[]]) { $fileTime = [BitConverter]::ToInt64($canAttemptUpdateAfter, 0) $canAttemptUpdateAfter = [DateTime]::FromFileTime($fileTime). ToUniversalTime() } elseif ($canAttemptUpdateAfter -is [long]) { $canAttemptUpdateAfter = [DateTime]::FromFileTime($canAttemptUpdateAfter). ToUniversalTime() } } captura { Write-Warning "Não foi possível converter CanAttemptUpdateAfter FILETIME em DateTime" } } Write-Host "Pode Tentar Atualizar Após: $canAttemptUpdateAfter" } captura { Write-Warning "CanAttemptUpdateAfter registry key not found or inaccessible" $canAttemptUpdateAfter = $null Write-Host "Pode Tentar Atualizar Após: Não Disponível" }
# Event Logs: System Log (10 values: 16-25)
# 16-25. Event Log queries # IDs de Evento: N.º 1801 - Atualização iniciada, reinício necessário N.º 1808 – Atualização concluída com êxito N.º 1795 – O firmware devolveu o erro (código de erro de captura) N.º 1796 – Erro registado com o código de erro (código de captura) N.º 1800 – Reinício necessário (NÃO é um erro – a atualização prosseguirá após o reinício) N.º 1802 – Problema de firmware conhecido– atualização bloqueada (capture KI_<número> de SkipReason) N.º 1803 – Atualização KEK correspondente não encontrada (o OEM precisa de fornecer KEK assinado por PK) # PS Version: 3.0+ | Administração: Pode ser necessário para o Registo do sistema | Requisitos de Sistema: Nenhum experimente { # Consultar todos os IDs de eventos de Arranque Seguro relevantes $allEventIds = @(1795, 1796, 1800, 1801, 1802, 1803, 1808) $events = @(Get-WinEvent -FilterHashtable @{LogName='System'; ID=$allEventIds} -MaxEvents 50 -ErrorAction Stop)
if ($events.Count -eq 0) { Write-Warning "Não foram encontrados eventos de Arranque Seguro no Registo do sistema" $latestEventId = $null $bucketId = $null $confidence = $null $skipReasonKnownIssue = $null $event 1801Count = 0 $event 1808Count = 0 $event 1795Count = 0 $event 1795ErrorCode = $null $event 1796Count = 0 $event 1796ErrorCode = $null $event 1800Count = 0 $rebootPending = $false $event 1802Count = 0 $knownIssueId = $null $event 1803Count = 0 $missingKEK = $false Write-Host "ID do Evento Mais Recente: Não Disponível" Write-Host "ID do Registo: Não Disponível" Write-Host "Confiança: Não Disponível" Write-Host "Contagem de Eventos 1801: 0" Write-Host "Contagem de Eventos 1808: 0" } senão { # 16. LatestEventId $latestEvent = $events | Sort-Object TimeCreated -Descending | Select-Object -Primeiro 1 if ($null -eq $latestEvent) { Write-Warning "Não foi possível determinar o evento mais recente" $latestEventId = $null Write-Host "ID do Evento Mais Recente: Não Disponível" } senão { $latestEventId = $latestEvent.Id Write-Host "ID do Evento Mais Recente: $latestEventId" }
# 17. BucketID - Extracted from Event 1801/1808 if ($null -ne $latestEvent -and $null -ne $latestEvent.Message) { if ($latestEvent.Message -match 'BucketId:\s*(.+)') { $bucketId = $matches[1]. Cortar() Write-Host "ID do Registo: $bucketId" } senão { Write-Warning "BucketId não encontrado na mensagem de evento" $bucketId = $null Write-Host "ID do Registo: Não Encontrado no Evento" } } senão { Write-Warning "O evento ou mensagem mais recente é nulo, não é possível extrair BucketId" $bucketId = $null Write-Host "ID do Registo: Não Disponível" }
# 18. Confidence - Extracted from Event 1801/1808 if ($null -ne $latestEvent -and $null -ne $latestEvent.Message) { if ($latestEvent.Message -match 'BucketConfidenceLevel:\s*(.+)') { $confidence = $matches[1]. Cortar() Write-Host "Confiança: $confidence" } senão { Write-Warning "Nível de confiança não encontrado na mensagem de evento" $confidence = $null Write-Host "Confiança: Não Encontrado no Evento" } } senão { Write-Warning "O evento ou mensagem mais recente é nulo, não é possível extrair Confiança" $confidence = $null Write-Host "Confiança: Não Disponível" }
# 18b. SkipReason - Extract KI_<number> from SkipReason in the same event as BucketId # Esta ação captura IDs de Problemas Conhecidos que aparecem juntamente com BucketId/Confidence (não apenas o Evento 1802) $skipReasonKnownIssue = $null if ($null -ne $latestEvent -and $null -ne $latestEvent.Message) { if ($latestEvent.Message -match 'SkipReason:\s*(KI_\d+)') { $skipReasonKnownIssue = $matches[1] Write-Host "Problema Conhecido skipReason: $skipReasonKnownIssue" -ForegroundColor Amarelo } }
# 19. Event1801Count $event 1801Array = @($events | Where-Object {$_. ID -eq 1801}) $event 1801Count = $event 1801Array.Count Write-Host "Contagem de Eventos 1801: $event 1801Count"
# 20. Event1808Count $event 1808Array = @($events | Where-Object {$_. ID -eq 1808}) $event 1808Count = $event 1808Array.Count Write-Host "Contagem de Eventos 1808: $event 1808Count" # Initialize error event variables (Inicializar variáveis de eventos de erro) $event 1795Count = 0 $event 1795ErrorCode = $null $event 1796Count = 0 $event 1796ErrorCode = $null $event 1800Count = 0 $rebootPending = $false $event 1802Count = 0 $knownIssueId = $null $event 1803Count = 0 $missingKEK = $false # Apenas marcar para eventos de erro se a atualização NÃO estiver concluída # Ignore a análise de erros se: 1808 for o evento mais recente OU UEFICA2023Status for "Atualizado" $updateComplete = ($latestEventId -eq 1808) ou ($uefica 2023Status -eq "Atualizado") if (-not $updateComplete) { Write-Host "Update not complete - checking for error events..." -ForegroundColor Yellow # 21. Evento1795 – Erro de Firmware (código de erro de captura) $event 1795Array = @($events | Where-Object {$_. ID -eq 1795}) $event 1795Count = $event 1795Array.Count if ($event 1795Count -gt 0) { $latestEvent 1795 = $event 1795Array | Sort-Object TimeCreated -Descending | Select-Object -Primeiro 1 if ($latestEvent 1795.Message -match '(?:error|code|status)[:\s]*(?:0x)? ([0-9A-Fa-f]{8}|[0-9A-Fa-f]+)') { $event 1795ErrorCode = $matches[1] } Write-Host "Contagem do Evento 1795 (Erro de Firmware): $event 1795Count" $(se ($event 1795ErrorCode) { "Código: $event 1795ErrorCode" }) } # 22. Evento1796 – Código de Erro Registado (código de erro de captura) $event 1796Array = @($events | Where-Object {$_. ID -eq 1796}) $event 1796Count = $event 1796Array.Count if ($event 1796Count -gt 0) { $latestEvent 1796 = $event 1796Array | Sort-Object TimeCreated -Descending | Select-Object -Primeiro 1 if ($latestEvent 1796.Message -match '(?:error|code|status)[:\s]*(?:0x)? ([0-9A-Fa-f]{8}|[0-9A-Fa-f]+)') { $event 1796ErrorCode = $matches[1] } Write-Host "Contagem do Evento 1796 (Erro Registado): $event 1796Count" $(se ($event 1796ErrorCode) { "Código: $event 1796ErrorCode" }) } # 23. Evento1800 – Reinício Necessário (NÃO é um erro – a atualização irá prosseguir após o reinício) $event 1800Array = @($events | Where-Object {$_. ID -eq 1800}) $event 1800Count = $event 1800Array.Count $rebootPending = $event 1800Count -gt 0 se ($rebootPending) { Write-Host "Evento 1800 (Reinício Pendente): A atualização irá prosseguir após o reinício" -ForegroundColor Cyan } # 24. Evento1802 – Problema de Firmware Conhecido (capture KI_<número> de SkipReason) $event 1802Array = @($events | Where-Object {$_. ID -eq 1802}) $event 1802Count = $event 1802Array.Count if ($event 1802Count -gt 0) { $latestEvent 1802 = $event 1802Array | Sort-Object TimeCreated -Descending | Select-Object -Primeiro 1 if ($latestEvent 1802.Message -match 'SkipReason:\s*(KI_\d+)') { $knownIssueId = $matches[1] } Write-Host "Número de Eventos 1802 (Problema de Firmware Conhecido): $event 1802Count" $(se ($knownIssueId) { "KI: $knownIssueId" }) } # 25. Evento1803 – Atualização kek em falta (o OEM precisa de fornecer KEK assinado pelo PK) $event 1803Array = @($events | Where-Object {$_. ID -eq 1803}) $event 1803Count = $event 1803Array.Count $missingKEK = $event 1803Count -gt 0 se ($missingKEK) { Write-Host "Evento 1803 (KEK em Falta): O OEM precisa de fornecer KEK assinado por PK" -ForegroundColor Amarelo } } senão { Write-Host "Atualização concluída (Evento 1808 ou Estado=Atualizado) – a ignorar a análise de erros" -ForegroundColor Green } } } captura { Write-Warning "Erro ao obter registos de eventos. Pode exigir privilégios de administrador: $_" $latestEventId = $null $bucketId = $null $confidence = $null $skipReasonKnownIssue = $null $event 1801Count = 0 $event 1808Count = 0 $event 1795Count = 0 $event 1795ErrorCode = $null $event 1796Count = 0 $event 1796ErrorCode = $null $event 1800Count = 0 $rebootPending = $false $event 1802Count = 0 $knownIssueId = $null $event 1803Count = 0 $missingKEK = $false Write-Host "ID do Evento Mais Recente: Erro" Write-Host "ID do Registo: Erro" Write-Host "Confiança: Erro" Write-Host "Contagem de Eventos 1801: 0" Write-Host "Contagem de Eventos 1808: 0" }
# WMI/CIM Queries (5 values)
# 26. OSVersion # PS Versão: 3.0+ (utilize Get-WmiObject para 2.0) | Administração: Não | Requisitos de Sistema: Nenhum experimente { $osInfo = Get-CimInstance Win32_OperatingSystem -ErrorAction Stop if ($null -eq $osInfo -or [string]::IsNullOrEmpty($osInfo.Version)) { Write-Warning "Não foi possível obter a versão do SO" $osVersion = "Desconhecido" } senão { $osVersion = $osInfo.Version } Write-Host "Versão do SO: $osVersion" } captura { # CIM may fail in some environments - use fallback $osVersion = [System.Environment]::OSVersion.Version.ToString() if ([string]::IsNullOrEmpty($osVersion)) { $osVersion = "Unknown" } Write-Host "Versão do SO: $osVersion" }
# 27. LastBootTime # PS Versão: 3.0+ (utilize Get-WmiObject para 2.0) | Administração: Não | Requisitos de Sistema: Nenhum experimente { $osInfo = Get-CimInstance Win32_OperatingSystem -ErrorAction Stop if ($null -eq $osInfo -or $null -eq $osInfo.LastBootUpTime) { Write-Warning "Não foi possível obter a hora do último arranque" $lastBootTime = $null Write-Host "Hora do Último Arranque: Não Disponível" } senão { $lastBootTime = $osInfo.LastBootUpTime Write-Host "Hora do Último Arranque: $lastBootTime" } } captura { # CIM may fail in some environments - use fallback experimente { $lastBootTime = (Get-Process -Id 0 -ErrorAction SilentlyContinue). StartTime } captura { $lastBootTime = $null } if ($lastBootTime) { Write-Host "Last Boot Time: $lastBootTime" } else { Write-Host "Last Boot Time: Not Available" } }
# 28. BaseBoardManufacturer # PS Versão: 3.0+ (utilize Get-WmiObject para 2.0) | Administração: Não | Requisitos de Sistema: Nenhum experimente { $baseBoard = Get-CimInstance Win32_BaseBoard -ErrorAction Stop if ($null -eq $baseBoard -or [string]::IsNullOrEmpty($baseBoard.Manufacturer)) { Write-Warning "Não foi possível obter o fabricante da placa base" $baseBoardManufacturer = "Desconhecido" } senão { $baseBoardManufacturer = $baseBoard.Manufacturer } Write-Host "Fabricante da Placa Base: $baseBoardManufacturer" } captura { # CIM may fail - baseboard info is supplementary $baseBoardManufacturer = "Desconhecido" Write-Host "Fabricante da Placa Base: $baseBoardManufacturer" }
# 29. BaseBoardProduct # PS Versão: 3.0+ (utilize Get-WmiObject para 2.0) | Administração: Não | Requisitos de Sistema: Nenhum experimente { $baseBoard = Get-CimInstance Win32_BaseBoard -ErrorAction Stop if ($null -eq $baseBoard -or [string]::IsNullOrEmpty($baseBoard.Product)) { Write-Warning "Não foi possível obter o produto da placa base" $baseBoardProduct = "Desconhecido" } senão { $baseBoardProduct = $baseBoard.Product } Write-Host "Baseboard Product: $baseBoardProduct" } captura { # CIM may fail - baseboard info is supplementary $baseBoardProduct = "Desconhecido" Write-Host "Baseboard Product: $baseBoardProduct" }
# 30. SecureBootTaskEnabled # PS Version: All | Administração: Não | Requisitos de Sistema: a Tarefa Agendada existe # Verifica se a tarefa agendada Atualização de Arranque Seguro está ativada $secureBootTaskEnabled = $null $secureBootTaskStatus = "Desconhecido" experimente { $taskOutput = schtasks.exe /Query /TN "\Microsoft\Windows\PI\Secure-Boot-Update" /FO CSV 2>&1 if ($LASTEXITCODE -eq 0) { $taskData = $taskOutput | ConverterFrom-Csv se ($taskData) { $secureBootTaskStatus = $taskData.Status $secureBootTaskEnabled = ($taskData.Status -eq 'Ready' -or $taskData.Status -eq 'Running') } } senão { $secureBootTaskStatus = "NotFound" $secureBootTaskEnabled = $false } if ($secureBootTaskEnabled -eq $false) { Write-Host "SecureBoot Update Task: $secureBootTaskStatus (Enabled: $secureBootTaskEnabled)" -ForegroundColor Yellow } senão { Write-Host "SecureBoot Update Task: $secureBootTaskStatus (Enabled: $secureBootTaskEnabled)" -ForegroundColor Green } } captura { $secureBootTaskStatus = "Erro" $secureBootTaskEnabled = $false Write-Host "SecureBoot Update Task: Error checking - $_" -ForegroundColor Red }
# 31. WinCS Key Status (F33E0C8E002 - Secure Boot Certificate Update) # PS Version: All | Administração: Sim (para consulta) | Requisitos de Sistema: WinCsFlags.exe $wincsKeyApplied = $null $wincsKeyStatus = "Desconhecido" experimente { # Verifique as localizações comuns para WinCsFlags.exe $wincsFlagsPath = $null $possiblePaths = @( "$env:SystemRoot\System32\WinCsFlags.exe", "$env:SystemRoot\SysWOW64\WinCsFlags.exe" ) foreach ($p no $possiblePaths) { if (Test-Path $p) { $wincsFlagsPath = $p; break } } se ($wincsFlagsPath) { # Chave específica da consulta – requer direitos de administrador $queryOutput = & $wincsFlagsPath /query --key F33E0C8E002 2>&1 $queryOutputStr = $queryOutput -join "'n" if ($LASTEXITCODE -eq 0) { # Verifique se a chave está aplicada (procure "Configuração Ativa" ou indicador semelhante) if ($queryOutputStr -match "Active Configuration.*:.*enabled" -or $queryOutputStr -match "Configuration.*applied") { $wincsKeyApplied = $true $wincsKeyStatus = "Aplicado" Write-Host "WinCS Key F33E0C8E002: Applied" -ForegroundColor Green } elseif ($queryOutputStr -match "not found|Sem configuração") { $wincsKeyApplied = $false $wincsKeyStatus = "NotApplied" Write-Host "WinCS Key F33E0C8E002: Not Applied" -ForegroundColor Yellow } senão { #Key exists - marcar saída para o estado $wincsKeyApplied = $true $wincsKeyStatus = "Aplicado" Write-Host "WinCS Key F33E0C8E002: Applied" -ForegroundColor Green } } senão { # Verificar se existem mensagens de erro específicas if ($queryOutputStr -match "Access denied|administrator") { $wincsKeyStatus = "AccessDenied" Write-Host "WinCS Key F33E0C8E002: Access denied (run as admin)" -ForegroundColor DarkGray } elseif ($queryOutputStr -match "not found|Sem configuração") { $wincsKeyApplied = $false $wincsKeyStatus = "NotApplied" Write-Host "WinCS Key F33E0C8E002: Not Applied" -ForegroundColor Yellow } senão { $wincsKeyStatus = "QueryFailed" Write-Host "WinCS Key F33E0C8E002: Query failed" -ForegroundColor Red } } } senão { $wincsKeyStatus = "WinCsFlagsNotFound" Write-Host "WinCS Key F33E0C8E002: WinCsFlags.exe not found" -ForegroundColor Gray } } captura { $wincsKeyStatus = "Erro" Write-Host "WinCS Key F33E0C8E002: Error checking - $_" -ForegroundColor Red }
# ============================================================================= # Deteção de Remediação – Estado saída & Código de Saída # =============================================================================
# Build status object from all collected inventory data $status = [encomendado]@{ UEFICA2023Status = $uefica 2023Status UEFICA2023Error = $uefica 2023Error UEFICA2023ErrorEvent = $uefica 2023ErrorEvent AvailableUpdates = se ($null -ne $availableUpdates) { $availableUpdatesHex } senão { $null } AvailableUpdatesPolicy = se ($null -ne $availableUpdatesPolicy) { $availableUpdatesPolicyHex } senão { $null } Hostname = $hostname CollectionTime = se ($collectionTime -is [datetime]) { $collectionTime.ToString("o") } else { "$collectionTime" } SecureBootEnabled = $secureBootEnabled HighConfidenceOptOut = $highConfidenceOptOut MicrosoftUpdateManagedOptIn = $microsoftUpdateManagedOptIn OEMManufacturerName = $oemManufacturerName OEMModelSystemFamily = $oemModelSystemFamily OEMModelNumber = $oemModelNumber FirmwareVersion = $firmwareVersion FirmwareReleaseDate = $firmwareReleaseDate OSArchitecture = $osArchitecture CanAttemptUpdateAfter = se ($canAttemptUpdateAfter -is [datetime]) { $canAttemptUpdateAfter.ToString("o") } else { "$canAttemptUpdateAfter" } LatestEventId = $latestEventId BucketId = $bucketId Confiança = $confidence SkipReasonKnownIssue = $skipReasonKnownIssue # KI_<número> de SkipReason no evento BucketId Event1801Count = $event 1801Count Event1808Count = $event 1808Count # Eventos de erro com detalhes capturados Event1795Count = $event 1795Count # O firmware devolveu o erro Event1795ErrorCode = $event 1795ErrorCode # Código de erro do firmware Event1796Count = $event 1796Contar # Código de erro registado Event1796ErrorCode = $event 1796ErrorCode # Código de erro capturado Event1800Count = $event 1800Count # Reinício necessário (NÃO é um erro) RebootPending = $rebootPending # Verdadeiro se o Evento 1800 estiver presente Event1802Count = $event 1802Count # Problema de firmware conhecido KnownIssueId = número $knownIssueId # KI_<> de SkipReason Event1803Count = $event 1803Count # Atualização KEK em falta MissingKEK = $missingKEK # OEM precisa de fornecer KEK assinado por PK OSVersion = $osVersion LastBootTime = se ($lastBootTime -is [datetime]) { $lastBootTime.ToString("o") } else { "$lastBootTime" } BaseBoardManufacturer = $baseBoardManufacturer BaseBoardProduct = $baseBoardProduct SecureBootTaskEnabled = $secureBootTaskEnabled SecureBootTaskStatus = $secureBootTaskStatus WinCSKeyApplied = $wincsKeyApplied # Verdadeiro se for aplicada F33E0C8E002 chave WinCSKeyStatus = $wincsKeyStatus # Aplicado, NotApplied, WinCsFlagsNotFound, etc. }
# Output the status - For data aggregation $jsonOutput = $status | ConvertTo-Json -Comprimir
# If OutputPath provided, save to file; otherwise output to stdout if (-not [string]::IsNullOrEmpty($OutputPath)) { # Validate OutputPath - ignore se parece com um pedido de ajuda ou se tem carateres inválidos if ($OutputPath -match '^[/\-]' -or $OutputPath -match '[<>:"|? *]') { Write-Host "OutputPath especificado inválido, a exportar para stdout" -ForegroundColor Yellow Write-Output $jsonOutput if ($secureBootEnabled -and $uefica 2023Status -eq "Updated") { exit 0 } else { exit 1 } } # Certifique-se de que a pasta de saída existe if (-not (Test-Path $OutputPath)) { experimente { New-Item -ItemType Directory -Path $OutputPath -Force | Out-Null } captura { Write-Warning "Não foi possível criar a pasta de saída: $OutputPath - $_" } } # Guardar no HOSTNAME_latest.json $outputFile = Join-Path $OutputPath "$($hostname)_latest.json" experimente { $jsonOutput | Out-File -FilePath $outputFile -Encoding UTF8 -Force Write-Host "JSON guardado em: $outputFile" -ForegroundColor Green } captura { Write-Warning "Não foi possível escrever no ficheiro: $outputFile - $_" # Recue para stdout Write-Output $jsonOutput } } senão { # Comportamento original - saída para stdout Write-Output $jsonOutput }
# Exit code: "Updated" is the success value per the playbook if ($secureBootEnabled -and $uefica 2023Status -eq "Updated") { sair de 0 # Sem problema } senão { sair de 1 # Com o problema }