scripts/Monitor_Windows_Updates.ps1
|
<# .SYNOPSIS Monitors Windows Update status and generates monitoring reports. .DESCRIPTION Comprehensive Windows Update monitoring script that checks available updates, auto-update configuration, installation history, and reboot status. Generates CSV reports and logs all findings through central logging system. .PARAMETER OutputDir Output directory for CSV reports (default: logs/). .PARAMETER WhatIf Show what would be done without making changes. .EXAMPLE .\Monitor_Windows_Updates.ps1 .\Monitor_Windows_Updates.ps1 -OutputDir "C:\Reports" .\Monitor_Windows_Updates.ps1 -WhatIf .NOTES SCHEDULE: Weekly (e.g., Monday @ 08:00 AM) RUN AS: SYSTEM (Highest Privileges) DEPENDENCIES: Core module, System module functions #> param( [string]$OutputDir, [switch]$WhatIf ) $ErrorActionPreference = "Stop" $ProgressPreference = "SilentlyContinue" # Determine project root and output directory $scriptRoot = Split-Path -Path $PSScriptRoot -Parent $projectRoot = Split-Path -Path $scriptRoot -Parent if (-not $OutputDir) { $OutputDir = Join-Path -Path $projectRoot -ChildPath "logs" } # Import Core module for logging and error handling $corePath = Join-Path -Path $projectRoot -ChildPath "modules\Core.psm1" if (-not (Test-Path $corePath)) { Write-Error "Core module not found at $corePath" -ErrorAction Stop exit 1 } Import-Module $corePath -Force -ErrorAction Stop # Import System module for Update functions $systemPath = Join-Path -Path $projectRoot -ChildPath "modules\System.psm1" if (Test-Path $systemPath) { try { Import-Module $systemPath -Force -ErrorAction Stop } catch { Write-Log -Message "System module failed to load: $($_.Exception.Message)" ` -Level Warning -Caller $MyInvocation.MyCommand.Name Write-Output "[WARN] System functions unavailable - Update checks may fail" } } Write-Output "" Write-Output "==============================================================" Write-Output " WINHARDEN WINDOWS UPDATE MONITORING" Write-Output "==============================================================" Write-Output "" Write-Output "Start Time: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" Write-Output "" Write-Output "[1] CHECKING WINDOWS UPDATE STATUS" Write-Output "==============================================================" $status = "UP-TO-DATE" $updateCount = 0 $securityCount = 0 $criticalCount = 0 try { $updateStatus = Get-WindowsUpdateStatus $updateCount = $updateStatus.AvailableUpdates $securityCount = $updateStatus.SecurityUpdates $criticalCount = $updateStatus.CriticalUpdates Write-Output "" Write-Output "[OK] Search completed: Found $updateCount available updates" if ($updateCount -eq 0) { Write-Output "[OK] No updates pending" $status = "UP-TO-DATE" } else { Write-Output "" Write-Output "[WARN] UPDATES AVAILABLE" $status = "UPDATES-PENDING" Write-Output "" Write-Output "Update Breakdown:" Write-Output " Security Updates: $securityCount" Write-Output " Critical Updates: $criticalCount" Write-Output " Other Updates: $($updateStatus.OtherUpdates)" # Show important updates Write-Output "" Write-Output "Top 5 Important Updates:" $importantUpdates = @($updateStatus.SecurityUpdatesList) + @($updateStatus.CriticalUpdatesList) | Select-Object -First 5 foreach ($update in $importantUpdates) { $kbNumber = $update.KBArticleIDs[0] $kbPrefix = if ($kbNumber) { "KB$kbNumber" } else { "[No KB]" } Write-Output " * $kbPrefix : $($update.Title)" } } } catch { Write-Output "[WARN] Could not check Windows Update directly: $($_.Exception.Message)" Write-Log -Message "Failed to check Windows Update status: $($_.Exception.Message)" ` -Level Warning -Caller $MyInvocation.MyCommand.Name $status = "CHECK-FAILED" } Write-Output "" Write-Output "[2] CHECKING AUTO-UPDATE CONFIGURATION" Write-Output "==============================================================" $autoUpdateConfig = $null try { $autoUpdateConfig = Get-AutoUpdateConfiguration if ($autoUpdateConfig.PolicyValue) { Write-Output "[OK] Auto-Update Configuration: $($autoUpdateConfig.Description)" } else { Write-Output "[INFO] Auto-Update uses default Windows settings" } } catch { Write-Output "[WARN] Could not retrieve Auto-Update configuration: $($_.Exception.Message)" Write-Log -Message "Failed to retrieve Auto-Update configuration: $($_.Exception.Message)" ` -Level Warning -Caller $MyInvocation.MyCommand.Name } Write-Output "" Write-Output "[3] CHECKING LAST UPDATE INSTALLATION" Write-Output "==============================================================" try { $updateHistory = Get-UpdateHistory -Count 5 if ($updateHistory) { Write-Output "[OK] Recent updates installed:" foreach ($hotfix in $updateHistory) { $installDate = if ($hotfix.InstalledOn) { (Get-Date $hotfix.InstalledOn -Format "yyyy-MM-dd") } else { "Unknown" } Write-Output " * KB$($hotfix.HotFixID): $installDate" } } else { Write-Output "[WARN] Could not retrieve update history" Write-Log -Message "No update history found on system" -Level Warning -Caller $MyInvocation.MyCommand.Name } } catch { Write-Output "[WARN] Error retrieving update history: $($_.Exception.Message)" Write-Log -Message "Error retrieving update history: $($_.Exception.Message)" ` -Level Warning -Caller $MyInvocation.MyCommand.Name } Write-Output "" Write-Output "[4] SYSTEM REBOOT STATUS" Write-Output "==============================================================" $pendingReboot = $false try { $rebootStatus = Get-PendingRebootStatus $pendingReboot = $rebootStatus.IsPending if ($pendingReboot) { Write-Output "[WARN] PENDING REBOOT DETECTED" Write-Output $rebootStatus.Message if ($status -ne "CHECK-FAILED") { $status = "REBOOT-REQUIRED" } } else { Write-Output "[OK] No reboot required" } } catch { Write-Output "[WARN] Could not check reboot status: $($_.Exception.Message)" Write-Log -Message "Error checking reboot status: $($_.Exception.Message)" ` -Level Warning -Caller $MyInvocation.MyCommand.Name } Write-Output "" Write-Output "[5] UPDATE RECOMMENDATIONS" Write-Output "==============================================================" if ($updateCount -gt 0) { Write-Output "" Write-Output "[ACTION RECOMMENDED]" Write-Output " 1. Review the available updates listed above" Write-Output " 2. Install security/critical updates as soon as possible" Write-Output " 3. Schedule reboot during maintenance window" Write-Output " 4. Test system after updates are installed" } else { Write-Output "" Write-Output "[OK] System is current with all latest updates" } Write-Output "" Write-Output "[REPORT SUMMARY]" Write-Output "==============================================================" $reportSummary = @{ 'Scan_Date' = Get-Date -Format 'yyyy-MM-dd HH:mm:ss' 'Status' = $status 'Updates_Available' = $updateCount 'Security_Updates' = $securityCount 'Critical_Updates' = $criticalCount 'Reboot_Pending' = $pendingReboot 'Auto_Updates_Enabled' = if ($autoUpdateConfig -and $autoUpdateConfig.IsEnabled) { $true } else { $false } } Write-Output "" Write-Output "Scan Results:" $reportSummary | Format-Table -AutoSize Write-Log -Message "Windows Update monitoring scan completed. Status: $status (Updates: $updateCount, Security: $securityCount, Critical: $criticalCount, Reboot: $pendingReboot)" ` -Level Info -Caller $MyInvocation.MyCommand.Name # Save detailed report (unless -WhatIf is used) if (-not $WhatIf) { try { # Create output directory if needed if (-not (Test-Path $OutputDir -PathType Container)) { $null = New-Item -ItemType Directory -Path $OutputDir -Force -ErrorAction Stop } $reportDate = Get-Date -Format "yyyy-MM-dd_HH-mm-ss" $reportFile = Join-Path $OutputDir "Windows_Updates_$reportDate.csv" $reportSummary | Export-Csv -Path $reportFile -NoTypeInformation -ErrorAction Stop Write-Output "" Write-Output "[OK] Detailed report saved: $reportFile" Write-Log -Message "Report exported to: $reportFile" -Level Info -Caller $MyInvocation.MyCommand.Name } catch { Write-Output "" Write-Output "[WARN] Failed to save report: $($_.Exception.Message)" Write-Log -Message "Failed to export report: $($_.Exception.Message)" -Level Error -Caller $MyInvocation.MyCommand.Name } } else { Write-Output "" Write-Output "[WhatIf] Report would be saved to: $(Join-Path $OutputDir "Windows_Updates_*.csv")" } Write-Output "" Write-Output "End Time: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" Write-Output "==============================================================" # Exit with appropriate code based on status $exitCode = switch ($status) { 'UP-TO-DATE' { 0 } 'UPDATES-PENDING' { 1 } 'REBOOT-REQUIRED' { 2 } 'CHECK-FAILED' { 3 } default { 1 } } Write-Log -Message "Script exiting with code: $exitCode" -Level Info -Caller $MyInvocation.MyCommand.Name exit $exitCode |