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
        }
    }
}