Private/Get-WUUpdateHistory.ps1

function Get-WUUpdateHistory {
    <#
    .SYNOPSIS
        Retrieves and analyzes Windows Update installation history.
 
    .DESCRIPTION
        Analyzes Windows Update history to identify patterns, success rates,
        and recent activity. Provides insights into update reliability.
 
    .PARAMETER Days
        Number of days back to analyze update history. Default is 30 days.
 
    .PARAMETER LogPath
        Path to the log file for detailed logging.
 
    .EXAMPLE
        $history = Get-WUUpdateHistory -Days 60 -LogPath "C:\Logs\wu.log"
 
    .NOTES
        This is a private function used internally by the WindowsUpdateTools module.
        Returns comprehensive update history analysis.
    #>


    [CmdletBinding()]
    param(
        [int]$Days = 30,
        [string]$LogPath
    )

    Write-WULog -Message "Analyzing Windows Update history for last $Days days" -LogPath $LogPath

    # Initialize results object
    $results = [PSCustomObject]@{
        AnalysisPeriod = $Days
        TotalUpdates = 0
        SuccessfulUpdates = 0
        FailedUpdates = 0
        AbortedUpdates = 0
        UpdatesWithErrors = 0
        LastSuccessfulUpdate = $null
        LastFailedUpdate = $null
        DaysSinceLastSuccess = $null
        DaysSinceLastFailure = $null
        RecentUpdates = @()
        FailedUpdateDetails = @()
        UpdateFrequency = @{}
        CommonErrors = @()
        Issues = @()
        ErrorMessage = $null
    }

    try {
        Write-WULog -Message "Retrieving Windows Update history..." -LogPath $LogPath

        # Create Windows Update session
        $updateSession = New-Object -ComObject Microsoft.Update.Session
        $updateSearcher = $updateSession.CreateUpdateSearcher()
        
        # Get update history (limit to reasonable number to avoid performance issues)
        $historyCount = [math]::Min($Days * 5, 100)  # Estimate 5 updates per day max, cap at 100
        $updateHistory = $updateSearcher.QueryHistory(0, $historyCount)
        
        if ($updateHistory.Count -eq 0) {
            Write-WULog -Message "No update history found" -LogPath $LogPath
            return $results
        }

        Write-WULog -Message "Found $($updateHistory.Count) update history entries" -LogPath $LogPath

        $cutoffDate = (Get-Date).AddDays(-$Days)
        $recentHistory = @()

        # Process update history entries
        foreach ($entry in $updateHistory) {
            try {
                # Skip entries outside our analysis period
                if ($entry.Date -lt $cutoffDate) {
                    continue
                }

                # Determine result status
                $resultText = switch ($entry.ResultCode) {
                    0 { "Not Started" }
                    1 { "In Progress" }
                    2 { "Succeeded" }
                    3 { "Succeeded with Errors" }
                    4 { "Failed" }
                    5 { "Aborted" }
                    default { "Unknown ($($entry.ResultCode))" }
                }

                # Determine operation type
                $operationType = switch ($entry.Operation) {
                    1 { "Installation" }
                    2 { "Uninstallation" }
                    3 { "Other" }
                    default { "Unknown" }
                }

                # Extract KB number if available
                $kbNumber = $null
                if ($entry.Title -match 'KB(\d+)') {
                    $kbNumber = "KB$($matches[1])"
                }

                # Create history entry object
                $historyEntry = [PSCustomObject]@{
                    Date = $entry.Date
                    Title = $entry.Title
                    KBNumber = $kbNumber
                    Operation = $operationType
                    ResultCode = $entry.ResultCode
                    Result = $resultText
                    HResult = if ($entry.HResult) { "0x$($entry.HResult.ToString('X8'))" } else { $null }
                    ClientApplicationID = $entry.ClientApplicationID
                    ServerSelection = $entry.ServerSelection
                    ServiceID = $entry.ServiceID
                    Description = $entry.Description
                }

                $recentHistory += $historyEntry

                # Update counters
                $results.TotalUpdates++
                
                switch ($entry.ResultCode) {
                    2 { 
                        $results.SuccessfulUpdates++
                        if (-not $results.LastSuccessfulUpdate -or $entry.Date -gt $results.LastSuccessfulUpdate) {
                            $results.LastSuccessfulUpdate = $entry.Date
                        }
                    }
                    3 { 
                        $results.UpdatesWithErrors++
                        if (-not $results.LastSuccessfulUpdate -or $entry.Date -gt $results.LastSuccessfulUpdate) {
                            $results.LastSuccessfulUpdate = $entry.Date  # Still considered successful
                        }
                    }
                    4 { 
                        $results.FailedUpdates++
                        if (-not $results.LastFailedUpdate -or $entry.Date -gt $results.LastFailedUpdate) {
                            $results.LastFailedUpdate = $entry.Date
                        }
                        
                        # Add to failed update details
                        $results.FailedUpdateDetails += [PSCustomObject]@{
                            Date = $entry.Date
                            Title = $entry.Title
                            KBNumber = $kbNumber
                            HResult = $historyEntry.HResult
                            Operation = $operationType
                        }
                    }
                    5 { 
                        $results.AbortedUpdates++
                        if (-not $results.LastFailedUpdate -or $entry.Date -gt $results.LastFailedUpdate) {
                            $results.LastFailedUpdate = $entry.Date
                        }
                    }
                }

                # Track update frequency by month
                $monthKey = $entry.Date.ToString("yyyy-MM")
                if ($results.UpdateFrequency.ContainsKey($monthKey)) {
                    $results.UpdateFrequency[$monthKey]++
                } else {
                    $results.UpdateFrequency[$monthKey] = 1
                }

            }
            catch {
                Write-WULog -Message "Error processing history entry: $($_.Exception.Message)" -Level Warning -LogPath $LogPath
            }
        }

        # Store recent updates (last 10)
        $results.RecentUpdates = $recentHistory | Sort-Object Date -Descending | Select-Object -First 10

        # Calculate days since last success/failure
        if ($results.LastSuccessfulUpdate) {
            $results.DaysSinceLastSuccess = [math]::Round(((Get-Date) - $results.LastSuccessfulUpdate).TotalDays)
        }
        
        if ($results.LastFailedUpdate) {
            $results.DaysSinceLastFailure = [math]::Round(((Get-Date) - $results.LastFailedUpdate).TotalDays)
        }

        # Analyze common errors
        if ($results.FailedUpdateDetails.Count -gt 0) {
            $errorGroups = $results.FailedUpdateDetails | Where-Object { $_.HResult } | Group-Object HResult | Sort-Object Count -Descending
            $results.CommonErrors = $errorGroups | Select-Object -First 5 | ForEach-Object {
                [PSCustomObject]@{
                    ErrorCode = $_.Name
                    Occurrences = $_.Count
                    MostRecentFailure = ($_.Group | Sort-Object Date -Descending | Select-Object -First 1).Date
                    Description = Get-WUErrorCodeDescription -ErrorCode $_.Name
                }
            }
        }

        # Generate issues based on analysis
        if ($results.TotalUpdates -eq 0) {
            $results.Issues += "No update activity found in the last $Days days"
        }

        if ($results.FailedUpdates -gt $results.SuccessfulUpdates -and $results.TotalUpdates -gt 0) {
            $results.Issues += "More failed updates ($($results.FailedUpdates)) than successful ones ($($results.SuccessfulUpdates))"
        }

        if ($results.DaysSinceLastSuccess -and $results.DaysSinceLastSuccess -gt 30) {
            $results.Issues += "Last successful update was $($results.DaysSinceLastSuccess) days ago"
        }

        if ($results.FailedUpdates -gt 5) {
            $results.Issues += "High number of failed updates ($($results.FailedUpdates)) indicates persistent issues"
        }

        if ($results.UpdatesWithErrors -gt 0) {
            $results.Issues += "$($results.UpdatesWithErrors) updates completed with errors"
        }

        # Calculate success rate
        $successRate = if ($results.TotalUpdates -gt 0) { 
            [math]::Round((($results.SuccessfulUpdates + $results.UpdatesWithErrors) / $results.TotalUpdates) * 100, 1) 
        } else { 0 }

        # Summary logging
        Write-WULog -Message "Update history analysis completed:" -LogPath $LogPath
        Write-WULog -Message " Total updates: $($results.TotalUpdates)" -LogPath $LogPath
        Write-WULog -Message " Successful: $($results.SuccessfulUpdates)" -LogPath $LogPath
        Write-WULog -Message " Failed: $($results.FailedUpdates)" -LogPath $LogPath
        Write-WULog -Message " Aborted: $($results.AbortedUpdates)" -LogPath $LogPath
        Write-WULog -Message " With errors: $($results.UpdatesWithErrors)" -LogPath $LogPath
        Write-WULog -Message " Success rate: $successRate%" -LogPath $LogPath
        
        if ($results.LastSuccessfulUpdate) {
            Write-WULog -Message " Last successful update: $($results.LastSuccessfulUpdate.ToString('yyyy-MM-dd HH:mm:ss')) ($($results.DaysSinceLastSuccess) days ago)" -LogPath $LogPath
        } else {
            Write-WULog -Message " No successful updates found in analysis period" -Level Warning -LogPath $LogPath
        }

        if ($results.CommonErrors.Count -gt 0) {
            Write-WULog -Message " Most common errors:" -LogPath $LogPath
            foreach ($error in $results.CommonErrors | Select-Object -First 3) {
                Write-WULog -Message " $($error.ErrorCode) ($($error.Occurrences)x): $($error.Description)" -LogPath $LogPath
            }
        }

        if ($results.Issues.Count -gt 0) {
            Write-WULog -Message "Update history issues:" -LogPath $LogPath
            foreach ($issue in $results.Issues) {
                Write-WULog -Message " - $issue" -Level Warning -LogPath $LogPath
            }
        }

    }
    catch {
        $results.ErrorMessage = $_.Exception.Message
        Write-WULog -Message "Error analyzing update history: $($_.Exception.Message)" -Level Error -LogPath $LogPath
    }

    return $results
}