Remediate-SecureBootCA2023.ps1
|
<#PSScriptInfo .VERSION 2.0 .GUID 3c9f7b2e-4d1a-4e8c-9c22-8f7d1a6b4c91 .AUTHOR Mert Efe Kanlikilic .COMPANYNAME mertefekanlikilic.com .COPYRIGHT (c) 2026 Mert Efe Kanlikilic .TAGS SecureBoot UEFI Intune Remediation Compliance PCA2023 .LICENSEURI https://github.com/mertefekanlikilic .PROJECTURI https://github.com/mertefekanlikilic .DESCRIPTION Secure Boot CA 2023 certificate update remediation script for Intune compliance. #> <# .SYNOPSIS Secure Boot CA 2023 certificate update remediation script. .NOTES Author : Mert Efe Kanlikilic -- mertefekanlikilic.com Version : 2.0 Platform: Windows 11 | Secure Boot enabled devices Run as : SYSTEM, 64-bit PowerShell Date : May 2026 Registry paths: HKLM:\SYSTEM\CurrentControlSet\Control\SecureBoot AvailableUpdates : 0x5944 -- triggers CA2023 update RemediationTimestamp : First remediation date (ISO 8601) -- written once RemediationAttemptCount : Total run count LastNotificationDay : Day threshold of last toast sent Toast notification timeline: Day 1 : Informational -- restart requested Day 3 : Reminder -- restart still pending Day 5 : Urgent -- security update delayed Day 7+ : IT Support -- escalation required #> $LOG_PATH = "$env:ProgramData\Microsoft\IntuneManagementExtension\Logs\SecureBoot-Remediation.log" $EVENTLOG_SOURCE = "SecureBootCA2023" $EVENTLOG_LOG = "Application" $SECUREBOOT_KEY = "HKLM:\SYSTEM\CurrentControlSet\Control\SecureBoot" $SERVICING_KEY = "HKLM:\SYSTEM\CurrentControlSet\Control\SecureBoot\Servicing" $AVAILABLE_UPDATES = 0x5944 $TASK_PATH = "\Microsoft\Windows\PI\" $TASK_NAME = "Secure-Boot-Update" $TOAST_APPID = "{1AC14E77-02E7-4E5D-B744-2EB1AE5198B7}\WindowsPowerShell\v1.0\powershell.exe" $NOTIFY_DAY1 = 0 $NOTIFY_DAY3 = 3 $NOTIFY_DAY5 = 5 $NOTIFY_DAY7 = 7 $TOAST_TEMPLATES = @{ Day1 = @{ Title = "Security Update -- Restart Required" Message = "A Secure Boot certificate update has been applied to your device. Please restart your computer today to complete the update." Action1 = "Restart Now" Action2 = "Remind Me Later" Urgency = "default" } Day3 = @{ Title = "Security Update -- Restart Still Pending" Message = "Your Secure Boot certificate update is waiting for a restart. This is a security requirement. Please restart your computer as soon as possible." Action1 = "Restart Now" Action2 = "Remind Me Later" Urgency = "default" } Day5 = @{ Title = "Security Update -- Urgent Restart Needed" Message = "Your device security update has been pending for 5 days. Please restart your computer today. Continuing to delay may affect your device's security posture." Action1 = "Restart Now" Action2 = "I Understand" Urgency = "urgent" } Day7 = @{ Title = "Security Update -- IT Support Required" Message = "Your Secure Boot certificate update has not completed after multiple attempts. Please contact IT support or restart your device immediately. Reference: SecureBoot-CA2023." Action1 = "Restart Now" Action2 = "Contact IT Support" Urgency = "urgent" } } function Write-Log { param([string]$Message, [ValidateSet("INFO","WARN","ERROR")]$Level = "INFO") $ts = Get-Date -Format "yyyy-MM-dd HH:mm:ss" try { Add-Content -Path $LOG_PATH -Value "[$ts][$Level] $Message" -Encoding UTF8 } catch { } } function Write-EventEntry { param([string]$Message, [int]$EventId, [string]$EntryType = "Information") try { if (-not [System.Diagnostics.EventLog]::SourceExists($EVENTLOG_SOURCE)) { New-EventLog -LogName $EVENTLOG_LOG -Source $EVENTLOG_SOURCE -ErrorAction Stop } Write-EventLog -LogName $EVENTLOG_LOG -Source $EVENTLOG_SOURCE ` -EventId $EventId -EntryType $EntryType -Message $Message } catch { Write-Log "Event log write failed: $($_.Exception.Message)" -Level WARN } } function Get-RemediationMetadata { $meta = [PSCustomObject]@{ FirstRunDate = $null AttemptCount = 0 LastNotificationDay = -1 DaysSinceFirst = 0 } try { $sb = Get-ItemProperty -Path $SECUREBOOT_KEY -ErrorAction SilentlyContinue if ($sb.RemediationTimestamp) { $meta.FirstRunDate = [datetime]$sb.RemediationTimestamp $meta.DaysSinceFirst = [math]::Floor(((Get-Date) - $meta.FirstRunDate).TotalDays) } if ($sb.RemediationAttemptCount) { $meta.AttemptCount = [int]$sb.RemediationAttemptCount } if ($null -ne $sb.LastNotificationDay) { $meta.LastNotificationDay = [int]$sb.LastNotificationDay } } catch { } return $meta } function Set-RemediationMetadata { param([datetime]$FirstRunDate, [int]$AttemptCount, [int]$LastNotificationDay) try { if (-not (Test-Path $SECUREBOOT_KEY)) { New-Item -Path $SECUREBOOT_KEY -Force | Out-Null } $existing = (Get-ItemProperty -Path $SECUREBOOT_KEY -ErrorAction SilentlyContinue).RemediationTimestamp if (-not $existing) { Set-ItemProperty -Path $SECUREBOOT_KEY -Name "RemediationTimestamp" ` -Value $FirstRunDate.ToString("o") -Type String -Force } Set-ItemProperty -Path $SECUREBOOT_KEY -Name "RemediationAttemptCount" -Value $AttemptCount -Type DWord -Force Set-ItemProperty -Path $SECUREBOOT_KEY -Name "LastNotificationDay" -Value $LastNotificationDay -Type DWord -Force } catch { Write-Log "Metadata write failed: $($_.Exception.Message)" -Level WARN } } function Set-AvailableUpdates { try { if (-not (Test-Path $SECUREBOOT_KEY)) { New-Item -Path $SECUREBOOT_KEY -Force | Out-Null } $current = (Get-ItemProperty -Path $SECUREBOOT_KEY -Name "AvailableUpdates" ` -ErrorAction SilentlyContinue).AvailableUpdates if ($current -eq $AVAILABLE_UPDATES) { Write-Log "AvailableUpdates already 0x5944 -- skipping write" return $true } Set-ItemProperty -Path $SECUREBOOT_KEY -Name "AvailableUpdates" ` -Value $AVAILABLE_UPDATES -Type DWord -Force $verify = (Get-ItemProperty -Path $SECUREBOOT_KEY -Name "AvailableUpdates").AvailableUpdates if ($verify -eq $AVAILABLE_UPDATES) { Write-Log "AvailableUpdates set to 0x5944" return $true } Write-Log "AvailableUpdates verification failed -- got: $verify" -Level ERROR return $false } catch { Write-Log "AvailableUpdates set failed: $($_.Exception.Message)" -Level ERROR return $false } } function Start-SecureBootTask { try { $task = Get-ScheduledTask -TaskPath $TASK_PATH -TaskName $TASK_NAME -ErrorAction SilentlyContinue if ($null -eq $task) { Write-Log "Scheduled task not found: ${TASK_PATH}${TASK_NAME}" -Level WARN return $false } Start-ScheduledTask -TaskPath $TASK_PATH -TaskName $TASK_NAME -ErrorAction Stop Write-Log "Scheduled task triggered: ${TASK_PATH}${TASK_NAME}" return $true } catch { Write-Log "Scheduled task trigger failed: $($_.Exception.Message)" -Level WARN return $false } } function Get-NotificationTier { param([int]$DaysSince, [int]$LastNotifiedDay) if ($DaysSince -ge $NOTIFY_DAY7 -and $LastNotifiedDay -lt $NOTIFY_DAY7) { return "Day7" } elseif ($DaysSince -ge $NOTIFY_DAY5 -and $LastNotifiedDay -lt $NOTIFY_DAY5) { return "Day5" } elseif ($DaysSince -ge $NOTIFY_DAY3 -and $LastNotifiedDay -lt $NOTIFY_DAY3) { return "Day3" } elseif ($DaysSince -ge $NOTIFY_DAY1 -and $LastNotifiedDay -lt $NOTIFY_DAY1) { return "Day1" } return $null } function Send-ToastNotification { param([string]$Tier) $tmpl = $TOAST_TEMPLATES[$Tier] if (-not $tmpl) { Write-Log "Unknown toast tier: $Tier" -Level WARN; return } $scenarioAttr = if ($tmpl.Urgency -eq "urgent") { 'scenario="urgent"' } else { "" } $toastXml = @" <toast $scenarioAttr> <visual> <binding template="ToastGeneric"> <text>$($tmpl.Title)</text> <text>$($tmpl.Message)</text> </binding> </visual> <actions> <action content="$($tmpl.Action1)" arguments="restart-now" activationType="background"/> <action content="$($tmpl.Action2)" arguments="remind-later" activationType="background"/> </actions> </toast> "@ try { $activeUser = (Get-WmiObject -Class Win32_ComputerSystem -ErrorAction Stop).UserName if (-not $activeUser) { Write-Log "No active user -- skipping toast" -Level WARN; return } $xmlOneLine = $toastXml -replace "`r`n", " " -replace '"', '\"' $toastScript = @" `$xml = New-Object Windows.Data.Xml.Dom.XmlDocument `$xml.LoadXml("$xmlOneLine") `$toast = [Windows.UI.Notifications.ToastNotification]::new(`$xml) `$notifier = [Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier("$TOAST_APPID") `$notifier.Show(`$toast) "@ $encoded = [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($toastScript)) $userSid = (New-Object System.Security.Principal.NTAccount($activeUser)).Translate( [System.Security.Principal.SecurityIdentifier]).Value $tempTask = "SecureBoot-Toast-$(Get-Random)" $action = New-ScheduledTaskAction -Execute "powershell.exe" ` -Argument "-NonInteractive -WindowStyle Hidden -EncodedCommand $encoded" $principal = New-ScheduledTaskPrincipal -UserId $userSid -RunLevel Limited $settings = New-ScheduledTaskSettingsSet -MultipleInstances IgnoreNew Register-ScheduledTask -TaskName $tempTask -Action $action ` -Principal $principal -Settings $settings -Force | Out-Null Start-ScheduledTask -TaskName $tempTask Start-Sleep -Seconds 5 Unregister-ScheduledTask -TaskName $tempTask -Confirm:$false -ErrorAction SilentlyContinue Write-Log "Toast sent -- Tier: $Tier | User: $activeUser" } catch { Write-Log "Toast delivery failed: $($_.Exception.Message)" -Level WARN } } # --- Main --- try { Start-Transcript -Path $LOG_PATH -Append -Force | Out-Null } catch { } Write-Log "Remediation v2.0 starting -- $env:COMPUTERNAME" if (-not [Environment]::Is64BitProcess) { Write-Log "32-bit PowerShell -- 64-bit required" -Level ERROR Stop-Transcript -ErrorAction SilentlyContinue exit 1 } $sbEnabled = $false try { $sbEnabled = Confirm-SecureBootUEFI -ErrorAction Stop } catch { $sbEnabled = $false } Write-Log "Secure Boot enabled: $sbEnabled" if (-not $sbEnabled) { Write-Log "Secure Boot disabled -- skipping remediation" Stop-Transcript -ErrorAction SilentlyContinue exit 0 } try { $serv = Get-ItemProperty -Path $SERVICING_KEY -ErrorAction SilentlyContinue if ($serv.UEFICA2023Status -eq "Updated" -and [int]$serv.WindowsUEFICA2023Capable -eq 2) { Write-Log "Device already compliant -- skipping remediation" Stop-Transcript -ErrorAction SilentlyContinue exit 0 } } catch { } $meta = Get-RemediationMetadata Write-Log "Metadata -- FirstRun: $($meta.FirstRunDate) | Attempts: $($meta.AttemptCount) | Days: $($meta.DaysSinceFirst) | LastNotified: $($meta.LastNotificationDay)" $regOk = Set-AvailableUpdates if (-not $regOk) { Write-Log "Registry write failed -- aborting remediation" -Level ERROR Write-EventEntry -Message "AvailableUpdates write failed -- $env:COMPUTERNAME" -EventId 1023 -EntryType "Error" Stop-Transcript -ErrorAction SilentlyContinue exit 1 } $taskOk = Start-SecureBootTask if (-not $taskOk) { Write-Log "Scheduled task not triggered -- Windows will run it automatically every 12 hours" -Level WARN } $newCount = $meta.AttemptCount + 1 $firstRun = if ($meta.FirstRunDate) { $meta.FirstRunDate } else { Get-Date } $daysSince = if ($meta.FirstRunDate) { $meta.DaysSinceFirst } else { 0 } $tier = Get-NotificationTier -DaysSince $daysSince -LastNotifiedDay $meta.LastNotificationDay $newLastNotifiedDay = $meta.LastNotificationDay if ($tier) { Send-ToastNotification -Tier $tier $newLastNotifiedDay = switch ($tier) { "Day7" { $NOTIFY_DAY7 } "Day5" { $NOTIFY_DAY5 } "Day3" { $NOTIFY_DAY3 } "Day1" { $NOTIFY_DAY1 } } Write-Log "Toast tier: $tier -- LastNotificationDay updated: $newLastNotifiedDay" } else { Write-Log "No notification due this run (threshold not reached)" } Set-RemediationMetadata -FirstRunDate $firstRun -AttemptCount $newCount -LastNotificationDay $newLastNotifiedDay $eventMsg = "Remediation complete -- Attempts: $newCount | Days: $daysSince | Tier: $tier | $env:COMPUTERNAME" Write-EventEntry -Message $eventMsg -EventId 1020 -EntryType "Information" Write-Log $eventMsg Write-Log "Remediation done -- awaiting device restart" Stop-Transcript -ErrorAction SilentlyContinue exit 0 |