Note
IMPORTANT This article containing this sample script has been retired. Starting with the Windows updates released on and after May 12, 2026, the sample script is located in the %systemroot%\SecureBoot\ExampleRolloutScripts folder on your device.
Copy and paste this sample script and modify as needed for your environment:
Note
<#
.SYNOPSIS
Shows the current status of the Secure Boot rollout orchestrator..DESCRIPTION
Provides real-time visibility into the rollout progress:
- Current wave number and devices targeted
- Devices updated vs pending
- Blocked buckets requiring attention
- Recent activity log
- Dashboard link
Run this anytime to see how the rollout is progressing..PARAMETER ReportBasePath
Path to the report/state directory used by the orchestrator.PARAMETER ShowLog
Display recent log entries (last 50 lines).PARAMETER ShowBlocked
Show details of blocked buckets.PARAMETER ShowWaves
Show wave history with device counts.PARAMETER Watch
Continuously refresh status every N seconds.PARAMETER OpenDashboard
Open the latest HTML dashboard in browser.EXAMPLE
.\Get-SecureBootRolloutStatus.ps1 -ReportBasePath "C:\SecureBootReports".EXAMPLE
.\Get-SecureBootRolloutStatus.ps1 -ReportBasePath "C:\SecureBootReports" -Watch 30.EXAMPLE
.\Get-SecureBootRolloutStatus.ps1 -ReportBasePath "C:\SecureBootReports" -OpenDashboard
#>[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string]$ReportBasePath,
[Parameter(Mandatory = $false)]
[switch]$ShowLog,
[Parameter(Mandatory = $false)]
[switch]$ShowBlocked,
[Parameter(Mandatory = $false)]
[switch]$ShowWaves,
[Parameter(Mandatory = $false)]
[int]$Watch = 0,
[Parameter(Mandatory = $false)]
[switch]$OpenDashboard
)$ErrorActionPreference = "Stop"
$DownloadUrl = "https://aka.ms/getsecureboot"
$DownloadSubPage = "Deployment and Monitoring Samples"# Note: This script has no dependencies on other scripts.
For the complete toolset, download from: $DownloadUrl -> $DownloadSubPage
# ============================================================================
HELPER FUNCTIONS
============================================================================
function ConvertTo-Hashtable {
param([Parameter(ValueFromPipeline = $true)]$InputObject)
process {
if ($null -eq $InputObject) { return @{} }
if ($InputObject -is [System.Collections.IDictionary]) { return $InputObject }
if ($InputObject -is [PSCustomObject]) {
$hash = @{}
foreach ($prop in $InputObject.PSObject.Properties) {
$hash[$prop.Name] = ConvertTo-Hashtable $prop.Value
}
return $hash
}
if ($InputObject -is [System.Collections.IEnumerable] -and $InputObject -isnot [string]) {
return @($InputObject | ForEach-Object { ConvertTo-Hashtable $_ })
}
return $InputObject
}
}function Show-Status {
$stateDir = Join-Path $ReportBasePath "RolloutState"
$rolloutStatePath = Join-Path $stateDir "RolloutState.json"
$blockedBucketsPath = Join-Path $stateDir "BlockedBuckets.json"
Clear-Host
Write-Host ""
Write-Host ("=" * 80) -ForegroundColor Cyan
Write-Host " SECURE BOOT ROLLOUT STATUS" -ForegroundColor Cyan
Write-Host " $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" -ForegroundColor Gray
Write-Host ("=" * 80) -ForegroundColor Cyan
Write-Host ""
# Check if orchestrator task is running
$task = Get-ScheduledTask -TaskName "SecureBoot-Rollout-Orchestrator" -ErrorAction SilentlyContinue
if ($task) {
$taskState = $task.State
$color = if ($taskState -eq "Running") { "Green" } elseif ($taskState -eq "Ready") { "Yellow" } else { "Red" }
Write-Host "Scheduled Task: " -NoNewline
Write-Host $taskState -ForegroundColor $color
} else {
Write-Host "Scheduled Task: " -NoNewline
Write-Host "Not Installed" -ForegroundColor Gray
}
# Load rollout state
if (-not (Test-Path $rolloutStatePath)) {
Write-Host ""
Write-Host "No rollout state found. Orchestrator may not have started yet." -ForegroundColor Yellow
Write-Host "State path: $rolloutStatePath" -ForegroundColor Gray
return
}
$state = Get-Content $rolloutStatePath -Raw | ConvertFrom-Json | ConvertTo-Hashtable
Write-Host ""
Write-Host "ROLLOUT PROGRESS" -ForegroundColor Yellow
Write-Host ("-" * 40)
$status = $state.Status
$statusColor = switch ($status) {
"Completed" { "Green" }
"InProgress" { "Cyan" }
"NotStarted" { "Gray" }
default { "White" }
}
Write-Host "Status: " -NoNewline
Write-Host $status -ForegroundColor $statusColor
Write-Host "Current Wave: $($state.CurrentWave)"
Write-Host "Total Targeted: $($state.TotalDevicesTargeted)"
Write-Host "Total Updated: $($state.TotalDevicesUpdated)"
if ($state.StartedAt) {
Write-Host "Started: $($state.StartedAt)"
}
if ($state.LastAggregation) {
Write-Host "Last Check: $($state.LastAggregation)"
}
if ($state.CompletedAt) {
Write-Host "Completed: $($state.CompletedAt)" -ForegroundColor Green
}
# Show progress bar
if ($state.TotalDevicesTargeted -gt 0) {
$pct = if ($state.TotalDevicesUpdated -and $state.TotalDevicesTargeted) {
[math]::Round(($state.TotalDevicesUpdated / $state.TotalDevicesTargeted) * 100, 1)
} else { 0 }
Write-Host ""
Write-Host "Progress: " -NoNewline
$barWidth = 40
$filled = [math]::Floor($barWidth * $pct / 100)
Write-Host "[" -NoNewline
Write-Host ("█" * $filled) -ForegroundColor Green -NoNewline
Write-Host ("░" * ($barWidth - $filled)) -ForegroundColor DarkGray -NoNewline
Write-Host "] $pct%"
}
# Blocked buckets summary
if (Test-Path $blockedBucketsPath) {
$blocked = Get-Content $blockedBucketsPath -Raw | ConvertFrom-Json | ConvertTo-Hashtable
if ($blocked.Count -gt 0) {
Write-Host ""
Write-Host "BLOCKED BUCKETS: " -NoNewline -ForegroundColor Red
Write-Host "$($blocked.Count) buckets need attention" -ForegroundColor Red
if ($ShowBlocked) {
Write-Host ""
foreach ($key in $blocked.Keys) {
$info = $blocked[$key]
Write-Host " ► $key" -ForegroundColor Red
Write-Host " Reason: $($info.Reason)" -ForegroundColor Gray
Write-Host " Device: $($info.FailedDevice)" -ForegroundColor Gray
Write-Host " Since: $($info.BlockedAt)" -ForegroundColor Gray
}
} else {
Write-Host " Run with -ShowBlocked for details" -ForegroundColor Gray
}
}
}
# Wave history
if ($ShowWaves -and $state.WaveHistory -and $state.WaveHistory.Count -gt 0) {
Write-Host ""
Write-Host "WAVE HISTORY" -ForegroundColor Yellow
Write-Host ("-" * 40)
foreach ($wave in $state.WaveHistory) {
Write-Host "Wave $($wave.WaveNumber): " -NoNewline -ForegroundColor Cyan
Write-Host "$($wave.DeviceCount) devices" -NoNewline
Write-Host " - $($wave.StartedAt)" -ForegroundColor Gray
}
}
# Latest dashboard
$latestAggregation = Get-ChildItem -Path $ReportBasePath -Directory -Filter "Aggregation_*" -ErrorAction SilentlyContinue |
Sort-Object Name -Descending |
Select-Object -First 1
if ($latestAggregation) {
$dashboard = Get-ChildItem -Path $latestAggregation.FullName -Filter "*Dashboard*.html" -ErrorAction SilentlyContinue |
Select-Object -First 1
if ($dashboard) {
Write-Host ""
Write-Host "LATEST DASHBOARD" -ForegroundColor Yellow
Write-Host $dashboard.FullName -ForegroundColor Gray
if ($OpenDashboard) {
Start-Process $dashboard.FullName
}
}
}
# Recent log
if ($ShowLog) {
$logFile = Get-ChildItem -Path $stateDir -Filter "Orchestrator_*.log" -ErrorAction SilentlyContinue |
Sort-Object Name -Descending |
Select-Object -First 1
if ($logFile) {
Write-Host ""
Write-Host "RECENT LOG" -ForegroundColor Yellow
Write-Host ("-" * 40)
Get-Content $logFile.FullName -Tail 20 | ForEach-Object {
if ($_ -match '\[ERROR\]') {
Write-Host $_ -ForegroundColor Red
} elseif ($_ -match '\[WARN\]') {
Write-Host $_ -ForegroundColor Yellow
} elseif ($_ -match '\[OK\]') {
Write-Host $_ -ForegroundColor Green
} elseif ($_ -match '\[WAVE\]') {
Write-Host $_ -ForegroundColor Cyan
} else {
Write-Host $_ -ForegroundColor Gray
}
}
}
}
Write-Host ""
Write-Host ("-" * 80) -ForegroundColor DarkGray
if (-not $ShowLog -or -not $ShowWaves -or -not $ShowBlocked) {
Write-Host "Options: -ShowLog | -ShowWaves | -ShowBlocked | -OpenDashboard | -Watch 30" -ForegroundColor DarkGray
}
}# ============================================================================
MAIN
============================================================================
if (-not (Test-Path $ReportBasePath)) {
Write-Host "Report path not found: $ReportBasePath" -ForegroundColor Red
exit 1
}if ($Watch -gt 0) {
Write-Host "Watching status every $Watch seconds. Press Ctrl+C to stop." -ForegroundColor Cyan
while ($true) {
Show-Status
Start-Sleep -Seconds $Watch
}
} else {
Show-Status
}