dist/temp/WindowsUpdateTools/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 SkipLogAnalysis Skip direct log analysis execution. Useful for systems with known issues that require no further analysis or when running in automated scenarios. .PARAMETER SkipTroubleshooter Skip Windows Update troubleshooter execution for faster assessment. .PARAMETER SkipDetailedEventLogs Skip extended event log history analysis (30 days). When specified, only analyzes last 7 days instead of the default 30 days for faster assessment. .PARAMETER IncludeWindows11Compatibility Perform comprehensive Windows 11 compatibility assessment including CPU, TPM, Secure Boot, UEFI, memory, storage, and other requirements. Also checks for Microsoft-imposed compatibility holds. .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. .PARAMETER SkipServicesCheck Skip Windows Update services assessment. Useful for scenarios where service status is not relevant or to speed up the assessment. .PARAMETER SkipComponentStoreCheck Skip Windows Component Store health assessment. Useful for scenarios where DISM/SFC checks are not needed or to speed up the assessment. .PARAMETER SkipPendingUpdatesCheck Skip pending Windows Updates check. Useful for faster assessment when update status is not relevant to troubleshooting. .EXAMPLE Test-WindowsUpdateHealth Performs basic Windows Update health assessment with default settings. .EXAMPLE Test-WindowsUpdateHealth -SkipDetailedEventLogs -PassThru Performs assessment with limited event log analysis and returns structured results for further processing. .EXAMPLE Test-WindowsUpdateHealth -SkipServicesCheck -SkipComponentStoreCheck -SkipTroubleshooter Performs fast assessment by skipping time-consuming service, component store, and troubleshooter checks. .EXAMPLE Test-WindowsUpdateHealth -SkipPendingUpdatesCheck -SkipServicesCheck -SkipComponentStoreCheck Performs ultra-fast assessment by skipping most time-consuming checks for quick troubleshooting. .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. .EXAMPLE Test-WindowsUpdateHealth -IncludeWindows11Compatibility -PassThru Performs health assessment with Windows 11 compatibility check and returns structured results including upgrade readiness. .EXAMPLE $results = Test-WindowsUpdateHealth -IncludeWindows11Compatibility -PassThru if ($results.Windows11Compatibility -and -not $results.Windows11Compatibility.Compatible) { Write-Warning "System not ready for Windows 11: $($results.Windows11Compatibility.OverallAssessment)" } Performs comprehensive assessment including Windows 11 compatibility and checks upgrade readiness. .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.5.0 Requires Administrator privileges for complete analysis. Some features require .NET Framework 4.7.2+ for SetupDiag functionality. Performance: Windows 11 compatibility assessment adds approximately 30-60 seconds to the total assessment time but provides comprehensive upgrade readiness analysis. #> [CmdletBinding()] param( [Parameter(Position = 0)] [ValidateScript({ Test-Path $_ -PathType Container })] [string]$OutputPath = $script:DefaultOutputPath, [switch]$SkipSetupDiag, [switch]$SkipLogAnalysis, [switch]$SkipTroubleshooter, [switch]$SkipDetailedEventLogs, [switch]$IncludeWindows11Compatibility, [switch]$PassThru, [string]$LogPath, [switch]$SkipServicesCheck, [switch]$SkipComponentStoreCheck, [switch]$SkipPendingUpdatesCheck ) 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 Windows11Compatibility = $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, SkipLogAnalysis=$SkipLogAnalysis, SkipTroubleshooter=$SkipTroubleshooter, SkipDetailedEventLogs=$SkipDetailedEventLogs, SkipServicesCheck=$SkipServicesCheck, SkipComponentStoreCheck=$SkipComponentStoreCheck, SkipPendingUpdatesCheck=$SkipPendingUpdatesCheck" -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 if (-not $SkipServicesCheck) { 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) { $unhealthyServices = $serviceResults.Services | Where-Object { $_.Critical -and -not $_.Healthy } $results.Issues += New-WUHealthIssue -Type "Services" -Severity "Critical" -Description "$($serviceResults.CriticalServicesDown) critical Windows Update services not running" -Details @{ UnhealthyServices = $unhealthyServices UnhealthyServiceCount = $serviceResults.CriticalServicesDown TotalServicesChecked = $serviceResults.Services.Count } $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 if (-not $SkipPendingUpdatesCheck) { 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" -Details @{ PendingUpdates = $pendingUpdates TotalCount = $pendingUpdates.Count ImportantUpdates = @($pendingUpdates | Where-Object { $_.Importance -eq "Important" -or $_.Importance -eq "Critical" }) UpdateSizes = @($pendingUpdates | ForEach-Object { [PSCustomObject]@{ Title = $_.Title; SizeMB = [math]::Round($_.MaxDownloadSize / 1MB, 2) } }) } } } 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 ($SkipDetailedEventLogs) { 7 } else { 30 } $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" -Details @{ FailedUpdates = $historyResults.FailedUpdates SuccessfulUpdates = $historyResults.SuccessfulUpdates TotalUpdates = $historyResults.TotalUpdates SuccessRate = $historyResults.SuccessRate CommonErrors = $historyResults.CommonErrors FailedUpdateDetails = $historyResults.FailedUpdateDetails LastFailedUpdate = $historyResults.LastFailedUpdate } $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 ($SkipDetailedEventLogs) { 7 } else { 30 } $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" -Details @{ SignificantErrorCodes = $eventLogResults.SignificantErrorCodes CommonFailures = $eventLogResults.CommonFailures CriticalErrors = $eventLogResults.CriticalErrors ErrorEvents = $eventLogResults.ErrorEvents TotalEvents = $eventLogResults.TotalEvents UpdateAttempts = $eventLogResults.UpdateAttempts FailedUpdates = $eventLogResults.FailedUpdates LastFailedUpdate = $eventLogResults.LastFailedUpdate AnalysisPeriod = $eventLogResults.AnalysisPeriod } $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" -Details @{ SignificantErrorCodes = $eventLogResults.SignificantErrorCodes CommonFailures = $eventLogResults.CommonFailures TotalEvents = $eventLogResults.TotalEvents ErrorEvents = $eventLogResults.ErrorEvents WarningEvents = $eventLogResults.WarningEvents UpdateAttempts = $eventLogResults.UpdateAttempts FailedUpdates = $eventLogResults.FailedUpdates SuccessfulUpdates = $eventLogResults.SuccessfulUpdates LastFailedUpdate = $eventLogResults.LastFailedUpdate AnalysisPeriod = $eventLogResults.AnalysisPeriod } $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 if (-not $SkipComponentStoreCheck) { 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 # Check for any CBS corruption indicators (even if DISM health checks pass) $shouldCreateIssue = $false $corruptionDetails = @() # CBS log corruption indicators are always significant if ($componentResults.CBSLogIssues -gt 0 -and $componentResults.CBSLogIndicators -and $componentResults.CBSLogIndicators.Count -gt 0) { $shouldCreateIssue = $true # Provide count only to avoid overwhelming output (details available in log file) $corruptionDetails += "$($componentResults.CBSLogIssues) corruption indicators found in CBS.log" } # DISM health check failures if (-not $componentResults.HealthCheckPassed) { $shouldCreateIssue = $true $corruptionDetails += "DISM health check failed" } if (-not $componentResults.ScanHealthPassed) { $shouldCreateIssue = $true $corruptionDetails += "DISM scan health failed" } # General corruption detection flag if ($componentResults.CorruptionDetected) { $shouldCreateIssue = $true } if ($shouldCreateIssue) { $detailedDescription = if ($corruptionDetails.Count -gt 0) { "Component store corruption detected: " + ($corruptionDetails -join ", ") } else { "Component store corruption detected" } $results.Issues += New-WUHealthIssue -Type "ComponentStore" -Severity "Critical" -Description $detailedDescription -Details @{ CorruptionDetected = $componentResults.CorruptionDetected HealthCheckPassed = $componentResults.HealthCheckPassed ScanHealthPassed = $componentResults.ScanHealthPassed CBSLogIssues = $componentResults.CBSLogIssues CBSLogIndicators = $componentResults.CBSLogIndicators Issues = $componentResults.Issues CheckHealthExitCode = $componentResults.CheckHealthExitCode ScanHealthExitCode = $componentResults.ScanHealthExitCode Remediation = "Run 'DISM /Online /Cleanup-Image /RestoreHealth' to repair component store corruption" } $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++ } } # Windows 11 Compatibility Assessment (if requested) if ($IncludeWindows11Compatibility) { Write-Progress -Activity "Windows Update Health Assessment" -Status "Checking Windows 11 compatibility..." -PercentComplete 65 Write-Verbose "Assessing Windows 11 upgrade compatibility" try { $win11CompatResults = Test-WUWindows11Compatibility -LogPath $LogPath $results.Windows11Compatibility = $win11CompatResults if (-not $win11CompatResults.Compatible) { $remediationSuggestions = @() # Generate specific remediation based on failed checks if ($win11CompatResults.FailedChecks -contains "TPM") { $remediationSuggestions += "Enable TPM 2.0 in BIOS/UEFI settings" } if ($win11CompatResults.FailedChecks -contains "SecureBoot") { $remediationSuggestions += "Enable Secure Boot in UEFI settings" } if ($win11CompatResults.FailedChecks -contains "Memory") { $remediationSuggestions += "Upgrade system memory to at least 4GB" } if ($win11CompatResults.FailedChecks -contains "Storage") { $remediationSuggestions += "Upgrade system storage to at least 64GB" } if ($win11CompatResults.FailedChecks -contains "BootMode") { $remediationSuggestions += "Convert from Legacy BIOS to UEFI boot" } if ($win11CompatResults.FailedChecks -contains "DiskPartitioning") { $remediationSuggestions += "Convert disk from MBR to GPT partitioning" } if ($win11CompatResults.FailedChecks -contains "CPU") { $remediationSuggestions += "Hardware upgrade required - CPU not supported" } $remediation = if ($remediationSuggestions.Count -gt 0) { $remediationSuggestions -join "; " } else { "Address failed requirements or contact Microsoft for compatibility hold information" } $results.Issues += New-WUHealthIssue -Type "Windows11Compatibility" -Severity "Warning" -Description "System not compatible with Windows 11" -Details @{ OverallAssessment = $win11CompatResults.OverallAssessment FailedChecks = $win11CompatResults.FailedChecks CompatibilityHolds = $win11CompatResults.CompatibilityHolds RequirementFailures = $win11CompatResults.Issues CheckDetails = $win11CompatResults.Checks } -Remediation $remediation $results.Warnings++ $results.IssuesFound = $true # Log specific failures Write-WULog -Message "Windows 11 compatibility issues detected:" -Level Warning -LogPath $LogPath foreach ($issue in $win11CompatResults.Issues) { Write-WULog -Message " - $issue" -Level Warning -LogPath $LogPath } } elseif ($win11CompatResults.WarningChecks.Count -gt 0) { $results.Issues += New-WUHealthIssue -Type "Windows11Compatibility" -Severity "Info" -Description "System compatible with Windows 11 with warnings" -Details @{ OverallAssessment = $win11CompatResults.OverallAssessment WarningChecks = $win11CompatResults.WarningChecks Recommendations = $win11CompatResults.Issues | Where-Object { $_ -like "Warning:*" } } Write-WULog -Message "Windows 11 compatibility: Compatible with warnings" -LogPath $LogPath foreach ($warning in $win11CompatResults.Issues | Where-Object { $_ -like "Warning:*" }) { Write-WULog -Message " $warning" -Level Warning -LogPath $LogPath } } else { Write-WULog -Message "Windows 11 compatibility: Fully compatible" -LogPath $LogPath } } catch { Write-WULog -Message "Failed to assess Windows 11 compatibility: $($_.Exception.Message)" -Level Warning -LogPath $LogPath $results.Issues += New-WUHealthIssue -Type "Windows11Compatibility" -Severity "Warning" -Description "Could not assess Windows 11 compatibility" $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 and Direct Log Analysis (if not skipped) if (-not $SkipLogAnalysis) { 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) { # Check if SetupDiag found a meaningful failure pattern or just couldn't match $hasSpecificFailurePattern = $setupDiagResults.FailureReasons -and $setupDiagResults.FailureReasons.Count -gt 0 -and -not ($setupDiagResults.FailureReasons -join "" -like "*unable to match*") # Determine severity based on whether SetupDiag found specific patterns $setupDiagSeverity = if ($hasSpecificFailurePattern) { "Critical" } else { "Warning" } $setupDiagDescription = if ($hasSpecificFailurePattern) { "$($setupDiagResults.FailuresDetected) upgrade failures detected by SetupDiag" } else { "SetupDiag detected upgrade attempt but couldn't identify specific failure pattern" } $results.Issues += New-WUHealthIssue -Type "SetupDiag" -Severity $setupDiagSeverity -Description $setupDiagDescription -Details @{ FailureReasons = $setupDiagResults.FailureReasons FailuresDetected = $setupDiagResults.FailuresDetected UpgradeAttemptDetected = $setupDiagResults.UpgradeAttemptDetected ExecutionSuccessful = $setupDiagResults.ExecutionSuccessful SetupDiagVersion = $setupDiagResults.SetupDiagVersion OutputFile = $setupDiagResults.OutputFile LogAnalysis = $setupDiagResults.LogAnalysis SystemInfo = $setupDiagResults.SystemInfo HasSpecificPattern = $hasSpecificFailurePattern } if ($setupDiagSeverity -eq "Critical") { $results.CriticalIssues++ } else { $results.Warnings++ } $results.IssuesFound = $true } # Check for enhanced blocking driver details in LogAnalysis if ($setupDiagResults.LogAnalysis) { $directAnalysis = $setupDiagResults.LogAnalysis | Where-Object { $_.Source -eq "DirectLogAnalysis" } if ($directAnalysis -and $directAnalysis.BlockingDriverDetails.Count -gt 0) { foreach ($driverDetail in $directAnalysis.BlockingDriverDetails) { # Check for signature status discrepancy between setup logs and actual status $isSignatureDiscrepancy = $false $discrepancyNote = "" # Look for false positive indicators in the targeted remediation $correspondingRemediation = $directAnalysis.TargetedRemediation | Where-Object { $_ -like "*$($driverDetail.OemInf)*" } if ($correspondingRemediation -and $correspondingRemediation -like "*False positive*") { $isSignatureDiscrepancy = $true $discrepancyNote = " - Setup logs incorrectly flagged as unsigned" } $driverSummary = if ($driverDetail.ResolvedSuccessfully) { if ($isSignatureDiscrepancy) { "$($driverDetail.ClassName) driver ($($driverDetail.ProviderName)) - $($driverDetail.OemInf)$discrepancyNote" } else { "$($driverDetail.ClassName) driver ($($driverDetail.ProviderName)) - $($driverDetail.OemInf)" } } else { "Driver $($driverDetail.OemInf) (resolution failed)" } # Any driver that is actually blocking an upgrade should be Critical severity, # regardless of signature discrepancy status, since it was found in setup failure logs $issueSeverity = "Critical" # Provide appropriate remediation based on signature status $remediation = if ($isSignatureDiscrepancy -and $driverDetail.IsSigned) { if ($driverDetail.ProviderName -eq "Microsoft" -or $driverDetail.SignerName -like "*Microsoft*") { "This Microsoft-signed driver is incorrectly flagged by Windows Setup compatibility scanner. Investigate Windows Update compatibility settings or contact Microsoft Support." } else { "Driver is properly signed but flagged by Setup. Check with manufacturer for Windows 11 24H2 compatibility updates or investigate compatibility hold issues." } } else { "Remove the blocking driver using Device Manager or pnputil before attempting Windows 11 upgrade" } $results.Issues += New-WUHealthIssue -Type "BlockingDriver" -Severity $issueSeverity -Description "Blocking driver detected: $driverSummary" -Details @{ OemInf = $driverDetail.OemInf OriginalName = $driverDetail.OriginalName ProviderName = $driverDetail.ProviderName ClassName = $driverDetail.ClassName DriverVersion = $driverDetail.DriverVersion DriverDate = $driverDetail.DriverDate IsSigned = $driverDetail.IsSigned SignerName = $driverDetail.SignerName DeviceDescriptions = $driverDetail.DeviceDescriptions AssociatedDevices = $driverDetail.AssociatedDevices HardwareIDs = $driverDetail.HardwareIDs ResolvedSuccessfully = $driverDetail.ResolvedSuccessfully IsSignatureDiscrepancy = $isSignatureDiscrepancy SetupLogClaim = if ($isSignatureDiscrepancy) { "Unsigned (Incorrect)" } else { "N/A" } ActualSignatureStatus = if ($driverDetail.IsSigned) { "Signed by $($driverDetail.SignerName)" } else { "Unsigned" } } -Remediation $remediation if ($issueSeverity -eq "Critical") { $results.CriticalIssues++ } else { $results.Warnings++ } $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++ } } else { Write-Verbose "Skipping SetupDiag.exe - running direct log analysis only" try { $directResults = Get-WUSetupLogsDirect -LogPath $LogPath if ($directResults.AnalysisSuccessful -and $directResults.FailuresDetected -gt 0) { $results.SetupDiagResults = [PSCustomObject]@{ ExecutionSuccessful = $true FailuresDetected = $directResults.FailuresDetected FailureReasons = $directResults.FailureReasons LogAnalysis = $directResults } # Add issues for blocking drivers with enhanced details if ($directResults.BlockingDriverDetails.Count -gt 0) { foreach ($driverDetail in $directResults.BlockingDriverDetails) { # Check for signature status discrepancy between setup logs and actual status $isSignatureDiscrepancy = $false $discrepancyNote = "" # Look for false positive indicators in the targeted remediation $correspondingRemediation = $directResults.TargetedRemediation | Where-Object { $_ -like "*$($driverDetail.OemInf)*" } if ($correspondingRemediation -and $correspondingRemediation -like "*False positive*") { $isSignatureDiscrepancy = $true $discrepancyNote = " - Setup logs incorrectly flagged as unsigned" } $driverSummary = if ($driverDetail.ResolvedSuccessfully) { if ($isSignatureDiscrepancy) { "$($driverDetail.ClassName) driver ($($driverDetail.ProviderName)) - $($driverDetail.OemInf)$discrepancyNote" } else { "$($driverDetail.ClassName) driver ($($driverDetail.ProviderName)) - $($driverDetail.OemInf)" } } else { "Driver $($driverDetail.OemInf) (resolution failed)" } # Any driver that is actually blocking an upgrade should be Critical severity, # regardless of signature discrepancy status, since it was found in setup failure logs $issueSeverity = "Critical" # Provide appropriate remediation based on signature status $remediation = if ($isSignatureDiscrepancy -and $driverDetail.IsSigned) { if ($driverDetail.ProviderName -eq "Microsoft" -or $driverDetail.SignerName -like "*Microsoft*") { "This Microsoft-signed driver is incorrectly flagged by Windows Setup compatibility scanner. Investigate Windows Update compatibility settings or contact Microsoft Support." } else { "Driver is properly signed but flagged by Setup. Check with manufacturer for Windows 11 24H2 compatibility updates or investigate compatibility hold issues." } } else { "Remove the blocking driver using Device Manager or pnputil before attempting Windows 11 upgrade" } $results.Issues += New-WUHealthIssue -Type "BlockingDriver" -Severity $issueSeverity -Description "Blocking driver detected: $driverSummary" -Details @{ OemInf = $driverDetail.OemInf OriginalName = $driverDetail.OriginalName ProviderName = $driverDetail.ProviderName ClassName = $driverDetail.ClassName DriverVersion = $driverDetail.DriverVersion DriverDate = $driverDetail.DriverDate IsSigned = $driverDetail.IsSigned SignerName = $driverDetail.SignerName DeviceDescriptions = $driverDetail.DeviceDescriptions AssociatedDevices = $driverDetail.AssociatedDevices HardwareIDs = $driverDetail.HardwareIDs ResolvedSuccessfully = $driverDetail.ResolvedSuccessfully IsSignatureDiscrepancy = $isSignatureDiscrepancy SetupLogClaim = if ($isSignatureDiscrepancy) { "Unsigned (Incorrect)" } else { "N/A" } ActualSignatureStatus = if ($driverDetail.IsSigned) { "Signed by $($driverDetail.SignerName)" } else { "Unsigned" } } -Remediation $remediation if ($issueSeverity -eq "Critical") { $results.CriticalIssues++ } else { $results.Warnings++ } $results.IssuesFound = $true } } } } catch { Write-WULog -Message "Direct log analysis failed: $($_.Exception.Message)" -Level Warning -LogPath $LogPath } } } # 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 # Add Windows 11 compatibility summary to logs if ($IncludeWindows11Compatibility -and $results.Windows11Compatibility) { Write-WULog -Message "Windows 11 Compatibility: $($results.Windows11Compatibility.OverallAssessment)" -LogPath $LogPath if ($results.Windows11Compatibility.FailedChecks.Count -gt 0) { Write-WULog -Message "Windows 11 Failed Requirements: $($results.Windows11Compatibility.FailedChecks -join ', ')" -Level Warning -LogPath $LogPath } if ($results.Windows11Compatibility.CompatibilityHolds.Count -gt 0) { Write-WULog -Message "Windows 11 Compatibility Holds: $($results.Windows11Compatibility.CompatibilityHolds.Count)" -Level Warning -LogPath $LogPath } } # Log signature discrepancy summary $signatureDiscrepancies = $results.Issues | Where-Object { $_.Type -eq "BlockingDriver" -and $_.Details -and $_.Details.IsSignatureDiscrepancy -eq $true } if ($signatureDiscrepancies.Count -gt 0) { Write-WULog -Message "SIGNATURE DISCREPANCY SUMMARY:" -LogPath $LogPath Write-WULog -Message "Found $($signatureDiscrepancies.Count) drivers incorrectly flagged as unsigned by Windows Setup:" -LogPath $LogPath foreach ($discrepancy in $signatureDiscrepancies) { $details = $discrepancy.Details # Build device information for log $deviceInfo = "" if ($details.DeviceDescriptions -and $details.DeviceDescriptions.Count -gt 0) { $deviceInfo = " (Devices: " + ($details.DeviceDescriptions -join ", ") + ")" } elseif ($details.AssociatedDevices -and $details.AssociatedDevices.Count -gt 0) { $presentDevices = $details.AssociatedDevices | Where-Object { $_.Present } | Select-Object -ExpandProperty Name if ($presentDevices.Count -gt 0) { $deviceInfo = " (Devices: " + ($presentDevices -join ", ") + ")" } } Write-WULog -Message " - $($details.OemInf): $($details.ClassName) by $($details.ProviderName)$deviceInfo" -LogPath $LogPath Write-WULog -Message " Setup claim: $($details.SetupLogClaim)" -LogPath $LogPath Write-WULog -Message " Actual status: $($details.ActualSignatureStatus)" -LogPath $LogPath } Write-WULog -Message "These appear to be Windows Setup compatibility scanner false positives." -LogPath $LogPath Write-WULog -Message "RECOMMENDED ACTION: Run 'Repair-WindowsUpdate -DriverCleanup' to resolve driver signature discrepancies." -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 # For component store issues, provide simplified CBS log corruption summary if ($issue.Type -eq "ComponentStore" -and $issue.Details -and $issue.Details.CBSLogIndicators -and $issue.Details.CBSLogIndicators.Count -gt 0) { Write-WULog -Message " CBS Log Corruption: $($issue.Details.CBSLogIndicators.Count) indicators found" -LogPath $LogPath if ($issue.Details.CBSLogIndicators.Count -le 3) { # Show all if few foreach ($indicator in $issue.Details.CBSLogIndicators) { Write-WULog -Message " $indicator" -LogPath $LogPath } } else { # Show sample if many $sampleIndicators = $issue.Details.CBSLogIndicators | Select-Object -First 2 Write-WULog -Message " Sample indicators (first 2 of $($issue.Details.CBSLogIndicators.Count)):" -LogPath $LogPath foreach ($indicator in $sampleIndicators) { Write-WULog -Message " $indicator" -LogPath $LogPath } Write-WULog -Message " ... and $($issue.Details.CBSLogIndicators.Count - 2) more (full list available in component store assessment details)" -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 } # Driver signature discrepancy summary $signatureDiscrepancies = $results.Issues | Where-Object { $_.Type -eq "BlockingDriver" -and $_.Details -and $_.Details.IsSignatureDiscrepancy -eq $true } if ($signatureDiscrepancies.Count -gt 0) { Write-Host "`nDriver Signature Analysis:" -ForegroundColor Cyan Write-Host "Found $($signatureDiscrepancies.Count) signed drivers incorrectly flagged by Windows Setup" -ForegroundColor Yellow foreach ($discrepancy in $signatureDiscrepancies) { $details = $discrepancy.Details # Build device information string $deviceInfo = "" if ($details.DeviceDescriptions -and $details.DeviceDescriptions.Count -gt 0) { $deviceInfo = " - " + ($details.DeviceDescriptions -join ", ") } elseif ($details.AssociatedDevices -and $details.AssociatedDevices.Count -gt 0) { $presentDevices = $details.AssociatedDevices | Where-Object { $_.Present } | Select-Object -ExpandProperty Name if ($presentDevices.Count -gt 0) { $deviceInfo = " - " + ($presentDevices -join ", ") } } # Use standard ASCII bullet point to avoid encoding issues Write-Host " - $($details.OemInf): $($details.ClassName) by $($details.ProviderName)$deviceInfo" -ForegroundColor Yellow } Write-Host "These are likely Windows Setup compatibility scanner false positives." -ForegroundColor Yellow Write-Host "`nRecommended Actions:" -ForegroundColor Cyan Write-Host " - Run: Repair-WindowsUpdate -DriverCleanup" -ForegroundColor White Write-Host " This will analyze and resolve driver signature discrepancies" -ForegroundColor Gray } # Windows 11 Compatibility Summary if ($IncludeWindows11Compatibility -and $results.Windows11Compatibility) { Write-Host "`nWindows 11 Compatibility: " -NoNewline if ($results.Windows11Compatibility.Compatible) { if ($results.Windows11Compatibility.WarningChecks.Count -gt 0) { Write-Host $results.Windows11Compatibility.OverallAssessment -ForegroundColor Yellow } else { Write-Host $results.Windows11Compatibility.OverallAssessment -ForegroundColor Green } } else { Write-Host $results.Windows11Compatibility.OverallAssessment -ForegroundColor Red if ($results.Windows11Compatibility.FailedChecks.Count -gt 0) { Write-Host "Failed requirements: $($results.Windows11Compatibility.FailedChecks -join ', ')" -ForegroundColor Red } } } # General Remediation Recommendations if ($results.IssuesFound) { Write-Host "`nRemediation Recommendations:" -ForegroundColor Cyan # Critical Issues if ($results.CriticalIssues -gt 0) { Write-Host "`nCritical Issues ($($results.CriticalIssues)):" -ForegroundColor Red # Check for specific issue types and provide targeted recommendations $hasServiceIssues = $results.Issues | Where-Object { $_.Type -eq "Service" -and $_.Severity -eq "Critical" } $hasComponentStoreIssues = $results.Issues | Where-Object { $_.Type -eq "ComponentStore" } $hasSetupDiagIssues = $results.Issues | Where-Object { $_.Type -eq "SetupDiag" } $hasBlockingDrivers = $results.Issues | Where-Object { $_.Type -eq "BlockingDriver" -and $_.Severity -eq "Critical" } if ($hasServiceIssues) { Write-Host " - Windows Update Services: Run 'Repair-WindowsUpdate -Services'" -ForegroundColor White } if ($hasComponentStoreIssues) { Write-Host " - Component Store: Run 'Repair-WindowsUpdate -ComponentStore'" -ForegroundColor White # Show simplified CBS log corruption information foreach ($componentIssue in $hasComponentStoreIssues) { if ($componentIssue.Details -and $componentIssue.Details.CBSLogIndicators -and $componentIssue.Details.CBSLogIndicators.Count -gt 0) { Write-Host " CBS Log Corruption: $($componentIssue.Details.CBSLogIndicators.Count) indicators found (details in log file)" -ForegroundColor Yellow } } } if ($hasSetupDiagIssues) { # Only provide SetupDiag recommendations if it found specific patterns # Skip generic recommendations when SetupDiag couldn't match patterns $setupDiagIssue = $hasSetupDiagIssues[0] # Get the first SetupDiag issue for analysis $hasSpecificPattern = $setupDiagIssue.Details -and $setupDiagIssue.Details.HasSpecificPattern if ($hasSpecificPattern) { $failureReasons = if ($setupDiagIssue.Details -and $setupDiagIssue.Details.FailureReasons) { $setupDiagIssue.Details.FailureReasons } else { @() } # Check for specific known issues that have targeted fixes $hasSystemPartitionIssue = $failureReasons | Where-Object { $_ -like "*Insufficient System Partition*" -or $_ -like "*System Reserved Partition*" -or $_ -like "*disk space*System*" } $hasDriverIssues = $failureReasons | Where-Object { $_ -like "*driver*" -or $_ -like "*blocking*" } $hasWSUSIssues = $failureReasons | Where-Object { $_ -like "*WSUS*" -or $_ -like "*Windows Update*configuration*" } # Provide specific recommendations based on detected issues if ($hasSystemPartitionIssue) { Write-Host " - System Partition Space: Run 'Repair-WindowsUpdate -SystemPartition'" -ForegroundColor White Write-Host " This will clean up the System Reserved Partition to free space" -ForegroundColor Gray } elseif ($hasDriverIssues) { Write-Host " - Driver-Related Setup Failures: Run 'Repair-WindowsUpdate -DriverCleanup'" -ForegroundColor White } elseif ($hasWSUSIssues) { Write-Host " - WSUS Configuration Issues: Run 'Repair-WindowsUpdate -WSUSConfig'" -ForegroundColor White } else { Write-Host " - Setup Failures: Run 'Repair-WindowsUpdate -Comprehensive'" -ForegroundColor White } } # Skip recommendation if SetupDiag couldn't identify specific patterns # The detailed log analysis will provide more targeted recommendations } if ($hasBlockingDrivers) { # Check if we have signature discrepancies among the critical blocking drivers $hasSignatureDiscrepancies = $results.Issues | Where-Object { $_.Type -eq "BlockingDriver" -and $_.Severity -eq "Critical" -and $_.Details -and $_.Details.IsSignatureDiscrepancy -eq $true } if ($hasSignatureDiscrepancies.Count -gt 0) { Write-Host " - Blocking Driver Signature Discrepancies: Run 'Repair-WindowsUpdate -DriverCleanup'" -ForegroundColor White Write-Host " This will resolve incorrectly flagged signed drivers" -ForegroundColor Gray } else { Write-Host " - Blocking Drivers: Run 'Repair-WindowsUpdate -DriverCleanup'" -ForegroundColor White } } } # Warning Issues if ($results.Warnings -gt 0) { Write-Host "`nWarning Issues ($($results.Warnings)):" -ForegroundColor Yellow $hasEventLogWarnings = $results.Issues | Where-Object { $_.Type -eq "EventLogs" } if ($hasEventLogWarnings) { Write-Host " - Event Log Errors: Review Windows Update history and consider 'Repair-WindowsUpdate -Comprehensive'" -ForegroundColor White } } # General recommendation Write-Host "`nGeneral Recommendation:" -ForegroundColor Cyan Write-Host " - For comprehensive repair: Run 'Repair-WindowsUpdate -Comprehensive'" -ForegroundColor White Write-Host " - This will address all detected issues systematically" -ForegroundColor Gray } 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 } } } |