Public/WindowsUpdate.ps1

#Requires -Version 5.1

<#
.SYNOPSIS
    Windows Update functions for DriverManagement module
#>


function Install-WindowsUpdates {
    <#
    .SYNOPSIS
        Installs Windows cumulative updates
    .DESCRIPTION
        Uses PSWindowsUpdate to install Windows updates, excluding drivers by default
    .PARAMETER IncludeDrivers
        Include drivers from Windows Update (not recommended with OEM drivers)
    .PARAMETER Categories
        Update categories to include
    .PARAMETER NoReboot
        Suppress automatic reboot
    .EXAMPLE
        Install-WindowsUpdates -NoReboot
    .OUTPUTS
        DriverUpdateResult object
    #>

    [CmdletBinding(SupportsShouldProcess)]
    [OutputType('DriverUpdateResult')]
    param(
        [Parameter()]
        [switch]$IncludeDrivers,
        
        [Parameter()]
        [string[]]$Categories = @('Security Updates', 'Critical Updates', 'Updates'),
        
        [Parameter()]
        [switch]$NoReboot
    )
    
    Assert-Elevation -Operation "Installing Windows Updates"
    
    $result = [DriverUpdateResult]::new()
    $result.CorrelationId = $script:CorrelationId
    
    # Ensure PSWindowsUpdate is available
    if (-not (Get-Module -ListAvailable -Name PSWindowsUpdate)) {
        try {
            Write-DriverLog -Message "Installing PSWindowsUpdate module" -Severity Info
            Install-Module -Name PSWindowsUpdate -Force -Scope AllUsers -ErrorAction Stop
        }
        catch {
            $result.Success = $false
            $result.Message = "Failed to install PSWindowsUpdate: $($_.Exception.Message)"
            $result.ExitCode = 1
            return $result
        }
    }
    
    Import-Module PSWindowsUpdate -Force
    
    Write-DriverLog -Message "Scanning for Windows Updates" -Severity Info
    
    $getParams = @{
        MicrosoftUpdate = $true
        Category = $Categories
        Verbose = $true
    }
    
    if (-not $IncludeDrivers) {
        $getParams.NotCategory = 'Drivers'
    }
    
    $updates = Get-WindowsUpdate @getParams
    
    if (-not $updates) {
        $result.Success = $true
        $result.Message = "No Windows Updates available"
        $result.ExitCode = 0
        Write-DriverLog -Message $result.Message -Severity Info
        return $result
    }
    
    Write-DriverLog -Message "Found $($updates.Count) Windows Updates" -Severity Info `
        -Context @{ Updates = ($updates | Select-Object KB, Title) }
    
    if ($PSCmdlet.ShouldProcess("$($updates.Count) Windows Updates", "Install")) {
        $installParams = @{
            MicrosoftUpdate = $true
            AcceptAll = $true
            IgnoreReboot = $true
            Verbose = $true
        }
        
        if (-not $IncludeDrivers) {
            $installParams.NotCategory = 'Drivers'
        }
        
        $installResults = Install-WindowsUpdate @installParams
        
        $rebootStatus = Get-WURebootStatus
        
        $result.Success = $true
        $result.Message = "Installed $($installResults.Count) Windows Updates"
        $result.UpdatesApplied = $installResults.Count
        $result.RebootRequired = $rebootStatus.RebootRequired
        $result.ExitCode = if ($result.RebootRequired) { 3010 } else { 0 }
        $result.Details = @{ Updates = ($installResults | Select-Object KB, Title, Result) }
    }
    
    Write-DriverLog -Message $result.Message -Severity Info -Context $result.ToHashtable()
    
    return $result
}

function Get-DriverComplianceStatus {
    <#
    .SYNOPSIS
        Gets the current driver compliance status
    .DESCRIPTION
        Reads the compliance status file
    .EXAMPLE
        Get-DriverComplianceStatus
    .OUTPUTS
        DriverComplianceStatus object
    #>

    [CmdletBinding()]
    [OutputType('DriverComplianceStatus')]
    param()
    
    $config = $script:ModuleConfig
    $compliancePath = $config.CompliancePath
    
    if (-not (Test-Path $compliancePath)) {
        $status = [DriverComplianceStatus]::new()
        $status.Status = [ComplianceStatus]::Unknown
        $status.Message = "No compliance check performed yet"
        return $status
    }
    
    try {
        $json = Get-Content $compliancePath -Raw
        return [DriverComplianceStatus]::FromJson($json)
    }
    catch {
        Write-DriverLog -Message "Failed to read compliance status: $($_.Exception.Message)" -Severity Warning
        $status = [DriverComplianceStatus]::new()
        $status.Status = [ComplianceStatus]::Error
        $status.Message = $_.Exception.Message
        return $status
    }
}

function Update-DriverComplianceStatus {
    <#
    .SYNOPSIS
        Updates the driver compliance status file
    .DESCRIPTION
        Writes current compliance state for detection scripts
    .PARAMETER Status
        Compliance status
    .PARAMETER UpdatesApplied
        Number of updates applied
    .PARAMETER UpdatesPending
        Number of updates pending
    .PARAMETER Message
        Status message
    .EXAMPLE
        Update-DriverComplianceStatus -Status Compliant -UpdatesApplied 5
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [ComplianceStatus]$Status,
        
        [Parameter()]
        [int]$UpdatesApplied = 0,
        
        [Parameter()]
        [int]$UpdatesPending = 0,
        
        [Parameter()]
        [string]$Message = ''
    )
    
    $config = $script:ModuleConfig
    
    # Ensure directory exists
    $complianceDir = Split-Path $config.CompliancePath -Parent
    if (-not (Test-Path $complianceDir)) {
        New-Item -Path $complianceDir -ItemType Directory -Force | Out-Null
    }
    
    $oemInfo = Get-OEMInfo
    
    $compliance = [DriverComplianceStatus]::new()
    $compliance.Version = $config.ModuleVersion
    $compliance.Status = $Status
    $compliance.OEM = $oemInfo.OEM
    $compliance.Model = $oemInfo.Model
    $compliance.UpdatesApplied = $UpdatesApplied
    $compliance.UpdatesPending = $UpdatesPending
    $compliance.Message = $Message
    $compliance.CorrelationId = $script:CorrelationId
    
    $compliance.ToHashtable() | ConvertTo-Json -Depth 3 | Set-Content -Path $config.CompliancePath -Encoding UTF8
    
    Write-DriverLog -Message "Updated compliance status: $Status" -Severity Info -Context $compliance.ToHashtable()
}