Public/Get-BuildUpgradeStatus.ps1
|
function Get-BuildUpgradeStatus { <# .SYNOPSIS Monitors the status of an in-progress Windows build upgrade. .DESCRIPTION Get-BuildUpgradeStatus checks for active Windows upgrade processes and reports current progress, stage, and recent activity. Useful for monitoring unattended upgrades initiated by Invoke-BuildUpgrade. .PARAMETER Detailed Display detailed recent log entries from the setup process. .PARAMETER Follow Continuously monitor the upgrade progress (refreshes every 10 seconds). Press Ctrl+C to exit. .EXAMPLE Get-BuildUpgradeStatus Shows current upgrade status and progress percentage. .EXAMPLE Get-BuildUpgradeStatus -Detailed Shows current status with recent log entries. .EXAMPLE Get-BuildUpgradeStatus -Follow Continuously monitors upgrade progress until completion. .OUTPUTS PSCustomObject with upgrade status information: - InProgress: Boolean indicating if upgrade is running - ProcessId: Setup process ID (if running) - Progress: Current progress percentage - Stage: Current upgrade stage - RuntimeMinutes: How long upgrade has been running - LastActivity: Most recent log entry - LogPath: Path to active setup log .NOTES Name: Get-BuildUpgradeStatus Author: WindowsUpdateTools Module Version: 1.0.0 Requires an active upgrade initiated by Invoke-BuildUpgrade or manual setup.exe execution. #> [CmdletBinding()] param( [switch]$Detailed, [switch]$Follow ) begin { $script:LoopCount = 0 } process { do { # Clear screen if following if ($Follow -and $script:LoopCount -gt 0) { Clear-Host } $script:LoopCount++ # Check for running setup processes $setupProcesses = Get-Process | Where-Object { $_.ProcessName -eq "setup" } $status = [PSCustomObject]@{ InProgress = $false ProcessId = $null Progress = $null ProgressAge = $null Stage = "Not Running" RuntimeMinutes = $null LastActivity = $null LogPath = $null StagingDirectory = $null RecentErrors = @() } if ($setupProcesses) { $mainProcess = $setupProcesses | Sort-Object StartTime | Select-Object -First 1 $status.InProgress = $true $status.ProcessId = $mainProcess.Id $status.RuntimeMinutes = [math]::Round(((Get-Date) - $mainProcess.StartTime).TotalMinutes, 1) } # Check for staging directory and logs if (Test-Path "C:\`$WINDOWS.~BT") { $status.StagingDirectory = "C:\`$WINDOWS.~BT" # Try to read setupact.log for progress $setupActLog = "C:\`$WINDOWS.~BT\Sources\Panther\setupact.log" if (Test-Path $setupActLog) { $status.LogPath = $setupActLog $status.InProgress = $true # Search entire file for most recent "Overall progress" (most accurate) # This is slower but necessary because progress can be far back in large logs $progressLines = Get-Content $setupActLog -ErrorAction SilentlyContinue | Select-String "Overall progress:\s*\[(\d+)%\]" | Select-Object -Last 1 # If no Overall progress, fall back to any progress in recent logs if (-not $progressLines) { $recentLogs = Get-Content $setupActLog -Tail 10000 -ErrorAction SilentlyContinue $progressLines = $recentLogs | Select-String "progress:\s*\[(\d+)%\]" | Select-Object -Last 1 } # Get recent logs for stage detection (after finding progress) $recentLogs = Get-Content $setupActLog -Tail 5000 -ErrorAction SilentlyContinue if ($progressLines) { if ($progressLines.Line -match '\[(\d+)%\]') { $status.Progress = [int]$matches[1] # Extract timestamp of progress update if ($progressLines.Line -match '^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})') { $progressTime = [DateTime]::ParseExact($matches[1], 'yyyy-MM-dd HH:mm:ss', $null) $status.ProgressAge = [math]::Round(((Get-Date) - $progressTime).TotalMinutes, 1) } } } # Determine stage based on log content and progress $lastFewLines = $recentLogs | Select-Object -Last 50 | Out-String if ($lastFewLines -match "DISM|driver") { $status.Stage = "Installing Drivers" } elseif ($lastFewLines -match "compatibility|compat|appraiser") { $status.Stage = "Compatibility Check" } elseif ($lastFewLines -match "download|acquire") { $status.Stage = "Downloading Updates" } elseif ($lastFewLines -match "install|apply") { $status.Stage = "Installing" } elseif ($lastFewLines -match "finalize|complete") { $status.Stage = "Finalizing" } elseif ($status.Progress -and $status.Progress -lt 30) { $status.Stage = "Initializing" } elseif ($status.Progress -and $status.Progress -lt 60) { $status.Stage = "Preparing Installation" } elseif ($status.Progress -and $status.Progress -lt 90) { $status.Stage = "Processing" } else { $status.Stage = "In Progress" } # Get last activity $lastLogEntry = $recentLogs | Select-Object -Last 1 if ($lastLogEntry) { $status.LastActivity = $lastLogEntry } # Check for errors $errorLog = "C:\`$WINDOWS.~BT\Sources\Panther\setuperr.log" if (Test-Path $errorLog) { $errors = Get-Content $errorLog -Tail 10 -ErrorAction SilentlyContinue if ($errors) { $status.RecentErrors = $errors } } } } # Display status Write-Host "`n=== Windows Build Upgrade Status ===" -ForegroundColor Cyan Write-Host "Timestamp: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')`n" -ForegroundColor Gray if ($status.InProgress) { Write-Host "Status: " -NoNewline Write-Host "IN PROGRESS" -ForegroundColor Green if ($status.ProcessId) { Write-Host "Process ID: $($status.ProcessId)" -ForegroundColor White } if ($status.RuntimeMinutes) { Write-Host "Runtime: $($status.RuntimeMinutes) minutes" -ForegroundColor White } Write-Host "Stage: " -NoNewline Write-Host $status.Stage -ForegroundColor Yellow if ($null -ne $status.Progress) { $progressBar = "[" + ("=" * [math]::Floor($status.Progress / 5)) + (" " * (20 - [math]::Floor($status.Progress / 5))) + "]" Write-Host "Progress: " -NoNewline Write-Host "$progressBar $($status.Progress)%" -ForegroundColor Cyan if ($status.ProgressAge) { Write-Host " (updated $($status.ProgressAge) min ago)" -ForegroundColor DarkGray } } if ($status.LastActivity) { Write-Host "`nLast Activity:" -ForegroundColor Gray Write-Host " $($status.LastActivity)" -ForegroundColor White } if ($Detailed -and $status.LogPath) { Write-Host "`nRecent Log Entries:" -ForegroundColor Gray Get-Content $status.LogPath -Tail 15 -ErrorAction SilentlyContinue | ForEach-Object { Write-Host " $_" -ForegroundColor DarkGray } } if ($status.RecentErrors.Count -gt 0) { Write-Host "`nRecent Errors:" -ForegroundColor Red $status.RecentErrors | ForEach-Object { Write-Host " $_" -ForegroundColor Yellow } } Write-Host "`nNote: System will reboot automatically when ready." -ForegroundColor Gray } else { Write-Host "Status: " -NoNewline Write-Host "NO ACTIVE UPGRADE" -ForegroundColor Yellow Write-Host "`nNo Windows upgrade process detected." -ForegroundColor Gray Write-Host "Check if upgrade completed or check logs at:" -ForegroundColor Gray Write-Host " C:\Windows\Panther\setupact.log" -ForegroundColor White Write-Host " C:\temp\WU-BuildUpgrade-*.log" -ForegroundColor White } if ($Follow -and $status.InProgress) { Write-Host "`nRefreshing in 10 seconds... (Press Ctrl+C to exit)" -ForegroundColor DarkGray Start-Sleep -Seconds 10 } } while ($Follow -and $status.InProgress) # Return status object return $status } } |