Public/Test-WindowsUpdateHealth.ps1
function Test-WindowsUpdateHealth { <# .SYNOPSIS Performs comprehensive Windows Update health assessment without making changes. .DESCRIPTION Test-WindowsUpdateHealth conducts a thorough analysis of Windows Update components, services, configuration, and recent activity to identify potential issues. This is a detection-only cmdlet that does not perform any remediation actions. .PARAMETER OutputPath Specifies the directory path where log files will be created. Default: C:\Windows\Temp .PARAMETER SkipSetupDiag Skip SetupDiag execution. Useful for systems without recent upgrade attempts or when .NET Framework 4.7.2+ is not available. .PARAMETER SkipTroubleshooter Skip Windows Update troubleshooter execution for faster assessment. .PARAMETER DetailedEventLogs Analyze extended event log history (30 days instead of 7 days). May take longer but provides more comprehensive failure analysis. .PARAMETER PassThru Return detailed results as PowerShell objects for further analysis. When not specified, only writes to host and log file. .PARAMETER LogPath Specifies a custom path for the log file. If not provided, automatically generates a timestamped log file in OutputPath. .EXAMPLE Test-WindowsUpdateHealth Performs basic Windows Update health assessment with default settings. .EXAMPLE Test-WindowsUpdateHealth -DetailedEventLogs -PassThru Performs detailed assessment with extended event log analysis and returns structured results for further processing. .EXAMPLE $results = Test-WindowsUpdateHealth -SkipSetupDiag -PassThru if ($results.IssuesFound) { Write-Warning "Windows Update issues detected" } Performs assessment without SetupDiag and checks if any issues were found. .EXAMPLE Test-WindowsUpdateHealth -OutputPath "C:\Logs" -Verbose Performs assessment with verbose logging to custom directory. .OUTPUTS When -PassThru is specified, returns a PSCustomObject with: - IssuesFound: Boolean indicating if any issues were detected - CriticalIssues: Count of critical issues found - Warnings: Count of warning-level issues - Issues: Array of detailed issue objects - SystemInfo: Basic system information - LastSuccessfulUpdate: Date of last successful update - Services: Windows Update service status - Configuration: Update configuration details .NOTES Name: Test-WindowsUpdateHealth Author: Anthony Balloi - CSOLVE Version: 1.0.0 Requires Administrator privileges for complete analysis. Some features require .NET Framework 4.7.2+ for SetupDiag functionality. #> [CmdletBinding()] param( [Parameter(Position = 0)] [ValidateScript({ Test-Path $_ -PathType Container })] [string]$OutputPath = $script:DefaultOutputPath, [switch]$SkipSetupDiag, [switch]$SkipTroubleshooter, [switch]$DetailedEventLogs, [switch]$PassThru, [string]$LogPath ) begin { # Initialize logging if (-not $LogPath) { $timestamp = Get-Date -Format 'yyyyMMdd-HHmmss' $LogPath = Join-Path $OutputPath "WU-HealthCheck-$timestamp.log" } # Initialize the assessment results object $results = [PSCustomObject]@{ IssuesFound = $false CriticalIssues = 0 Warnings = 0 Issues = @() SystemInfo = $null LastSuccessfulUpdate = $null Services = @() Configuration = $null SetupDiagResults = $null EventLogSummary = $null ComponentStoreHealth = $null TroubleshooterResults = $null AssessmentDate = Get-Date LogPath = $LogPath } # Start logging Write-WULog -Message "=== Windows Update Health Assessment Started ===" -LogPath $LogPath Write-WULog -Message "Version: $script:ModuleVersion" -LogPath $LogPath Write-WULog -Message "Parameters: OutputPath=$OutputPath, SkipSetupDiag=$SkipSetupDiag, SkipTroubleshooter=$SkipTroubleshooter, DetailedEventLogs=$DetailedEventLogs" -LogPath $LogPath $assessmentErrors = 0 } process { try { # System Information Assessment Write-Progress -Activity "Windows Update Health Assessment" -Status "Gathering system information..." -PercentComplete 10 Write-Verbose "Collecting system information" try { $results.SystemInfo = Get-WUSystemInfo -LogPath $LogPath Write-WULog -Message "System information collected successfully" -LogPath $LogPath } catch { Write-WULog -Message "Failed to gather system information: $($_.Exception.Message)" -Level Error -LogPath $LogPath $results.Issues += New-WUHealthIssue -Type "SystemInfo" -Severity "Warning" -Description "Could not gather complete system information" $results.Warnings++ $assessmentErrors++ } # Windows Update Services Assessment Write-Progress -Activity "Windows Update Health Assessment" -Status "Checking Windows Update services..." -PercentComplete 20 Write-Verbose "Analyzing Windows Update services" try { $serviceResults = Test-WUServices -LogPath $LogPath $results.Services = $serviceResults.Services if ($serviceResults.CriticalServicesDown -gt 0) { $results.Issues += New-WUHealthIssue -Type "Services" -Severity "Critical" -Description "$($serviceResults.CriticalServicesDown) critical Windows Update services not running" $results.CriticalIssues++ $results.IssuesFound = $true } if ($serviceResults.NonCriticalServicesDown -gt 0) { $results.Issues += New-WUHealthIssue -Type "Services" -Severity "Warning" -Description "$($serviceResults.NonCriticalServicesDown) non-critical Windows Update services not running" $results.Warnings++ $results.IssuesFound = $true } } catch { Write-WULog -Message "Failed to check Windows Update services: $($_.Exception.Message)" -Level Error -LogPath $LogPath $results.Issues += New-WUHealthIssue -Type "Services" -Severity "Critical" -Description "Could not assess Windows Update services" $results.CriticalIssues++ $results.IssuesFound = $true $assessmentErrors++ } # Pending Updates Check Write-Progress -Activity "Windows Update Health Assessment" -Status "Checking pending updates..." -PercentComplete 30 Write-Verbose "Checking for pending updates" try { $pendingUpdates = Get-WUPendingUpdates -LogPath $LogPath if ($pendingUpdates.Count -gt 0) { Write-WULog -Message "Found $($pendingUpdates.Count) pending updates" -LogPath $LogPath $results.Issues += New-WUHealthIssue -Type "PendingUpdates" -Severity "Info" -Description "$($pendingUpdates.Count) pending updates found" } } catch { Write-WULog -Message "Failed to check pending updates: $($_.Exception.Message)" -Level Warning -LogPath $LogPath $results.Issues += New-WUHealthIssue -Type "PendingUpdates" -Severity "Warning" -Description "Could not check for pending updates" $results.Warnings++ $assessmentErrors++ } # Update History Analysis Write-Progress -Activity "Windows Update Health Assessment" -Status "Analyzing update history..." -PercentComplete 40 Write-Verbose "Analyzing recent update history" try { $historyDays = if ($DetailedEventLogs) { 30 } else { 7 } $historyResults = Get-WUUpdateHistory -Days $historyDays -LogPath $LogPath $results.LastSuccessfulUpdate = $historyResults.LastSuccessfulUpdate if ($historyResults.FailedUpdates -gt 0) { $results.Issues += New-WUHealthIssue -Type "UpdateHistory" -Severity "Warning" -Description "$($historyResults.FailedUpdates) failed updates in recent history" $results.Warnings++ $results.IssuesFound = $true } if ($historyResults.DaysSinceLastSuccess -gt 30) { $results.Issues += New-WUHealthIssue -Type "UpdateHistory" -Severity "Critical" -Description "No successful updates in over 30 days" $results.CriticalIssues++ $results.IssuesFound = $true } } catch { $errorMessage = $_.Exception.Message Write-WULog -Message "Failed to analyze update history: $errorMessage" -Level Warning -LogPath $LogPath # Check for specific error types if ($errorMessage -like "*COM*" -or $errorMessage -like "*0x*") { Write-WULog -Message "This may be a COM object or Windows Update service issue. Try restarting Windows Update services." -Level Warning -LogPath $LogPath } $results.Issues += New-WUHealthIssue -Type "UpdateHistory" -Severity "Warning" -Description "Could not analyze update history" $results.Warnings++ $assessmentErrors++ } # Event Log Analysis Write-Progress -Activity "Windows Update Health Assessment" -Status "Analyzing event logs..." -PercentComplete 50 Write-Verbose "Analyzing Windows Update event logs" try { $eventLogDays = if ($DetailedEventLogs) { 30 } else { 7 } $eventLogResults = Get-WUEventLogs -Days $eventLogDays -LogPath $LogPath $results.EventLogSummary = $eventLogResults if ($eventLogResults.CriticalErrors -gt 0) { $results.Issues += New-WUHealthIssue -Type "EventLogs" -Severity "Critical" -Description "$($eventLogResults.CriticalErrors) critical errors in event logs" $results.CriticalIssues++ $results.IssuesFound = $true } if ($eventLogResults.ErrorEvents -gt 5) { $results.Issues += New-WUHealthIssue -Type "EventLogs" -Severity "Warning" -Description "High number of error events ($($eventLogResults.ErrorEvents)) in recent logs" $results.Warnings++ $results.IssuesFound = $true } } catch { $errorMessage = $_.Exception.Message Write-WULog -Message "Failed to analyze event logs: $errorMessage" -Level Warning -LogPath $LogPath # Check for specific error types if ($errorMessage -like "*Access*denied*" -or $errorMessage -like "*permission*") { Write-WULog -Message "This may be a permissions issue. Try running as Administrator." -Level Warning -LogPath $LogPath } elseif ($errorMessage -like "*event log*" -or $errorMessage -like "*log file*") { Write-WULog -Message "Event log service may not be running or logs may be corrupted." -Level Warning -LogPath $LogPath } $results.Issues += New-WUHealthIssue -Type "EventLogs" -Severity "Warning" -Description "Could not analyze event logs" $results.Warnings++ $assessmentErrors++ } # Component Store Health Check Write-Progress -Activity "Windows Update Health Assessment" -Status "Checking component store health..." -PercentComplete 60 Write-Verbose "Checking component store health" try { $componentResults = Test-WUComponentStore -LogPath $LogPath $results.ComponentStoreHealth = $componentResults if ($componentResults.CorruptionDetected) { $results.Issues += New-WUHealthIssue -Type "ComponentStore" -Severity "Critical" -Description "Component store corruption detected" $results.CriticalIssues++ $results.IssuesFound = $true } } catch { Write-WULog -Message "Failed to check component store health: $($_.Exception.Message)" -Level Warning -LogPath $LogPath $results.Issues += New-WUHealthIssue -Type "ComponentStore" -Severity "Warning" -Description "Could not check component store health" $results.Warnings++ $assessmentErrors++ } # WSUS Configuration Check Write-Progress -Activity "Windows Update Health Assessment" -Status "Checking update configuration..." -PercentComplete 70 Write-Verbose "Analyzing Windows Update configuration" try { $configResults = Get-WUConfiguration -LogPath $LogPath $results.Configuration = $configResults if ($configResults.Issues.Count -gt 0) { foreach ($issue in $configResults.Issues) { $results.Issues += $issue if ($issue.Severity -eq "Critical") { $results.CriticalIssues++ } else { $results.Warnings++ } $results.IssuesFound = $true } } } catch { Write-WULog -Message "Failed to check update configuration: $($_.Exception.Message)" -Level Warning -LogPath $LogPath $results.Issues += New-WUHealthIssue -Type "Configuration" -Severity "Warning" -Description "Could not analyze update configuration" $results.Warnings++ $assessmentErrors++ } # SetupDiag Analysis (if not skipped) if (-not $SkipSetupDiag) { Write-Progress -Activity "Windows Update Health Assessment" -Status "Running SetupDiag analysis..." -PercentComplete 80 Write-Verbose "Executing SetupDiag analysis" try { $setupDiagResults = Invoke-WUSetupDiag -OutputPath $OutputPath -LogPath $LogPath $results.SetupDiagResults = $setupDiagResults if ($setupDiagResults.FailuresDetected -gt 0) { $results.Issues += New-WUHealthIssue -Type "SetupDiag" -Severity "Critical" -Description "$($setupDiagResults.FailuresDetected) upgrade failures detected by SetupDiag" $results.CriticalIssues++ $results.IssuesFound = $true } } catch { Write-WULog -Message "SetupDiag analysis failed: $($_.Exception.Message)" -Level Warning -LogPath $LogPath $results.Issues += New-WUHealthIssue -Type "SetupDiag" -Severity "Warning" -Description "SetupDiag analysis could not be completed" $results.Warnings++ $assessmentErrors++ } } # Windows Update Troubleshooter (if not skipped) if (-not $SkipTroubleshooter) { Write-Progress -Activity "Windows Update Health Assessment" -Status "Running Windows Update troubleshooter..." -PercentComplete 90 Write-Verbose "Executing Windows Update troubleshooter" try { $troubleshooterResults = Invoke-WUTroubleshooter -LogPath $LogPath $results.TroubleshooterResults = $troubleshooterResults if ($troubleshooterResults.IssuesDetected -gt 0) { $resolvedCount = $troubleshooterResults.IssuesResolved $unresolvedCount = $troubleshooterResults.IssuesDetected - $resolvedCount if ($unresolvedCount -gt 0) { $results.Issues += New-WUHealthIssue -Type "Troubleshooter" -Severity "Warning" -Description "$unresolvedCount unresolved issues detected by troubleshooter" $results.Warnings++ $results.IssuesFound = $true } } } catch { Write-WULog -Message "Windows Update troubleshooter failed: $($_.Exception.Message)" -Level Warning -LogPath $LogPath $results.Issues += New-WUHealthIssue -Type "Troubleshooter" -Severity "Warning" -Description "Windows Update troubleshooter could not run" $results.Warnings++ $assessmentErrors++ } } # Ensure IssuesFound flag is set correctly if ($results.CriticalIssues -gt 0 -or $results.Warnings -gt 0) { $results.IssuesFound = $true } } catch { Write-WULog -Message "Critical error during health assessment: $($_.Exception.Message)" -Level Error -LogPath $LogPath $results.Issues += New-WUHealthIssue -Type "Assessment" -Severity "Critical" -Description "Critical error during health assessment" $results.CriticalIssues++ $results.IssuesFound = $true $assessmentErrors++ } finally { Write-Progress -Activity "Windows Update Health Assessment" -Completed } } end { # Generate summary Write-WULog -Message "=== ASSESSMENT SUMMARY ===" -LogPath $LogPath Write-WULog -Message "Issues Found: $($results.IssuesFound)" -LogPath $LogPath Write-WULog -Message "Critical Issues: $($results.CriticalIssues)" -LogPath $LogPath Write-WULog -Message "Warnings: $($results.Warnings)" -LogPath $LogPath Write-WULog -Message "Assessment Errors: $assessmentErrors" -LogPath $LogPath if ($results.Issues.Count -gt 0) { Write-WULog -Message "ISSUES DETECTED:" -LogPath $LogPath foreach ($issue in $results.Issues) { # Map Critical severity to Error for logging compatibility and ensure valid level $logLevel = switch ($issue.Severity) { "Critical" { "Error" } "Warning" { "Warning" } "Error" { "Error" } "Info" { "Info" } default { "Info" } } Write-WULog -Message " $($issue.Type): $($issue.Description)" -Level $logLevel -LogPath $LogPath } } else { Write-WULog -Message "No significant issues detected" -LogPath $LogPath } Write-WULog -Message "Log file saved to: $LogPath" -LogPath $LogPath Write-WULog -Message "=== Windows Update Assessment Completed ===" -LogPath $LogPath # Display summary to host Write-Host "`n=== Windows Update Assessment Results ===" -ForegroundColor Cyan if ($results.CriticalIssues -gt 0) { Write-Host "Critical Issues: $($results.CriticalIssues)" -ForegroundColor Red } if ($results.Warnings -gt 0) { Write-Host "Warnings: $($results.Warnings)" -ForegroundColor Yellow } if (-not $results.IssuesFound) { Write-Host "No significant issues detected" -ForegroundColor Green } Write-Host "Log file: $LogPath" -ForegroundColor Gray # Return results if PassThru requested if ($PassThru) { return $results } # Set exit code based on results if ($assessmentErrors -gt 5) { $global:LASTEXITCODE = $script:ExitCodes.MajorFailure } elseif ($assessmentErrors -gt 0) { $global:LASTEXITCODE = $script:ExitCodes.PartialFailure } else { $global:LASTEXITCODE = $script:ExitCodes.Success } } } |