Created
April 29, 2025 01:03
-
-
Save greenido/ece355aeb54583957f45a502d18e6e4b to your computer and use it in GitHub Desktop.
Windows Patch Management Information Script # Refined for improved structure, efficiency, and consistency
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
######################################################### | |
# Windows Patch Management Information Script | |
# Refined for improved structure, efficiency, and consistency | |
######################################################### | |
# Output setup | |
$OutputDir = "$env:USERPROFILE\Desktop\PatchManagementReport" | |
if (-not (Test-Path -Path $OutputDir)) { | |
New-Item -ItemType Directory -Path $OutputDir -Force | Out-Null | |
} | |
$timestamp = Get-Date -Format "yyyyMMdd-HHmmss" | |
$outputFile = "$OutputDir\PatchManagementReport-$timestamp.txt" | |
# Centralized output function | |
function Write-InfoOutput { | |
param([string]$Message) | |
Write-Host $Message | |
Add-Content -Path $outputFile -Value $Message | |
} | |
# Section header helper | |
function Write-SectionHeader { | |
param([string]$Title) | |
$separator = '=' * 80 | |
Write-InfoOutput "`n$separator`n$Title`n$separator" | |
} | |
# Initialize output file | |
"Windows Patch Management Report - $(Get-Date)" | Out-File -FilePath $outputFile | |
# Load PSWindowsUpdate if available | |
$PSWUAvailable = $false | |
try { | |
if (Get-Module -ListAvailable -Name PSWindowsUpdate) { | |
Import-Module PSWindowsUpdate -Force -ErrorAction Stop | |
$PSWUAvailable = $true | |
} | |
} catch { | |
Write-Host "PSWindowsUpdate module not available. Continuing without it." -ForegroundColor Yellow | |
} | |
# Function to safely retrieve registry values | |
function Get-RegistryValueSafe { | |
param($Path, $Name) | |
try { | |
(Get-ItemProperty -Path $Path -Name $Name -ErrorAction Stop).$Name | |
} catch { | |
return $null | |
} | |
} | |
# SYSTEM INFORMATION | |
Write-SectionHeader "SYSTEM INFORMATION" | |
Get-ComputerInfo | Select-Object WindowsProductName, WindowsVersion, OsHardwareAbstractionLayer, CsManufacturer, CsModel, BiosVersion | | |
Format-List | Out-String | Write-InfoOutput | |
# WINDOWS UPDATE SERVICE STATUS | |
Write-SectionHeader "WINDOWS UPDATE SERVICE STATUS" | |
try { | |
$updateService = Get-Service -Name wuauserv | |
Write-InfoOutput "Service Name: $($updateService.DisplayName)" | |
Write-InfoOutput "Status: $($updateService.Status)" | |
Write-InfoOutput "Startup Type: $(Get-WmiObject Win32_Service | Where-Object {$_.Name -eq 'wuauserv'} | Select-Object -ExpandProperty StartMode)" | |
} catch { | |
Write-InfoOutput "Failed to retrieve Windows Update service status: $_" | |
} | |
# WINDOWS UPDATE SETTINGS | |
Write-SectionHeader "WINDOWS UPDATE SETTINGS" | |
$WindowsUpdatePath = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate" | |
$AutoUpdatePath = "$WindowsUpdatePath\AU" | |
if (Test-Path $WindowsUpdatePath) { | |
Write-InfoOutput "Windows Update Policy Settings:" | |
Get-ItemProperty -Path $WindowsUpdatePath | Format-Table -AutoSize | Out-String | Write-InfoOutput | |
} | |
if (Test-Path $AutoUpdatePath) { | |
Write-InfoOutput "Automatic Update Settings:" | |
Get-ItemProperty -Path $AutoUpdatePath | Format-Table -AutoSize | Out-String | Write-InfoOutput | |
} | |
$WSUSServer = Get-RegistryValueSafe -Path $WindowsUpdatePath -Name "WUServer" | |
Write-InfoOutput "WSUS Server: $($WSUSServer ?? 'Not configured')" | |
# WINDOWS UPDATE CONFIGURATION (PSWindowsUpdate) | |
if ($PSWUAvailable) { | |
Write-SectionHeader "WINDOWS UPDATE CONFIGURATION (PSWindowsUpdate)" | |
Get-WUSettings | Format-List | Out-String | Write-InfoOutput | |
} | |
# WINDOWS UPDATE HISTORY | |
Write-SectionHeader "WINDOWS UPDATE HISTORY (LAST 50 UPDATES)" | |
try { | |
$UpdateSession = New-Object -ComObject Microsoft.Update.Session | |
$UpdateSearcher = $UpdateSession.CreateUpdateSearcher() | |
$UpdateHistoryCount = $UpdateSearcher.GetTotalHistoryCount() | |
if ($UpdateHistoryCount -gt 0) { | |
$Updates = $UpdateSearcher.QueryHistory(0, $UpdateHistoryCount) | | |
Select-Object Title, Description, Date, | |
@{Name="Operation"; Expression={switch($_.Operation){1{"Installation"};2{"Uninstallation"};3{"Other"};default{"Unknown"}}}}, | |
@{Name="Status"; Expression={switch($_.ResultCode){0{"Not Started"};1{"In Progress"};2{"Succeeded"};3{"Succeeded With Errors"};4{"Failed"};5{"Aborted"};default{"Unknown"}}}} | |
$Updates | Sort-Object Date -Descending | Select-Object -First 50 | Format-Table -AutoSize -Wrap | Out-String | Write-InfoOutput | |
} else { | |
Write-InfoOutput "No update history found." | |
} | |
} catch { | |
Write-InfoOutput "Error retrieving update history: $_" | |
} | |
# INSTALLED UPDATES | |
Write-SectionHeader "INSTALLED UPDATES/HOTFIXES" | |
try { | |
$hotfixes = Get-HotFix | Sort-Object InstalledOn -Descending | |
$hotfixes | Format-Table -AutoSize | Out-String | Write-InfoOutput | |
Write-InfoOutput "Total Installed Updates: $($hotfixes.Count)" | |
} catch { | |
Write-InfoOutput "Error retrieving installed updates: $_" | |
} | |
# LAST UPDATE CHECK/INSTALL | |
Write-SectionHeader "LAST WINDOWS UPDATE CHECK/INSTALL" | |
$WindowsUpdateResults = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\Results" | |
foreach ($type in @("Detect", "Download", "Install")) { | |
$path = "$WindowsUpdateResults\$type" | |
if (Test-Path $path) { | |
$time = Get-RegistryValueSafe -Path $path -Name "LastSuccessTime" | |
if ($time) { Write-InfoOutput "Last Update $type: $time" } | |
} | |
} | |
# PENDING UPDATES | |
Write-SectionHeader "PENDING UPDATES" | |
try { | |
if ($PSWUAvailable) { | |
$pendingUpdates = Get-WUList | |
if ($pendingUpdates) { | |
$pendingUpdates | Format-Table -AutoSize | Out-String | Write-InfoOutput | |
Write-InfoOutput "Total Pending Updates: $($pendingUpdates.Count)" | |
} else { | |
Write-InfoOutput "No pending updates found." | |
} | |
} else { | |
$pendingUpdates = (New-Object -ComObject Microsoft.Update.Session).CreateUpdateSearcher().Search("IsInstalled=0 and Type='Software'").Updates | |
if ($pendingUpdates.Count -gt 0) { | |
Write-InfoOutput "Found $($pendingUpdates.Count) pending updates:" | |
$pendingUpdates | ForEach-Object { Write-InfoOutput "- $($_.Title)" } | |
} else { | |
Write-InfoOutput "No pending updates found." | |
} | |
} | |
} catch { | |
Write-InfoOutput "Error retrieving pending updates: $_" | |
} | |
# WINDOWS DEFENDER STATUS | |
Write-SectionHeader "WINDOWS DEFENDER UPDATE STATUS" | |
try { | |
$defenderStatus = Get-MpComputerStatus -ErrorAction SilentlyContinue | |
if ($defenderStatus) { | |
Write-InfoOutput "Antivirus Signature Version: $($defenderStatus.AntivirusSignatureVersion)" | |
Write-InfoOutput "Antivirus Signature Last Updated: $($defenderStatus.AntivirusSignatureLastUpdated)" | |
Write-InfoOutput "Real-time Protection Enabled: $($defenderStatus.RealTimeProtectionEnabled)" | |
} else { | |
Write-InfoOutput "Windows Defender status information not available." | |
} | |
} catch { | |
Write-InfoOutput "Error retrieving Windows Defender status: $_" | |
} | |
# REBOOT REQUIRED STATUS | |
Write-SectionHeader "REBOOT REQUIRED STATUS" | |
$rebootIndicators = @( | |
@{ Name = "Component Based Servicing"; Path = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending" }, | |
@{ Name = "Windows Update"; Path = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired" }, | |
@{ Name = "SCCM Client"; Path = "HKLM:\SOFTWARE\Microsoft\SMS\Mobile Client\Reboot Management\RebootData" } | |
) | |
$rebootPending = $false | |
foreach ($indicator in $rebootIndicators) { | |
if (Test-Path $indicator.Path) { | |
Write-InfoOutput "$($indicator.Name): Reboot pending" | |
$rebootPending = $true | |
} else { | |
Write-InfoOutput "$($indicator.Name): No reboot pending" | |
} | |
} | |
$pendingFileRename = Get-RegistryValueSafe -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager" -Name "PendingFileRenameOperations" | |
if ($pendingFileRename) { | |
Write-InfoOutput "Pending File Rename Operations: Yes" | |
$rebootPending = $true | |
} else { | |
Write-InfoOutput "Pending File Rename Operations: No" | |
} | |
if ($rebootPending) { | |
Write-InfoOutput "OVERALL REBOOT STATUS: System restart IS required" | |
} else { | |
Write-InfoOutput "OVERALL REBOOT STATUS: No system restart required" | |
} | |
# WINDOWS UPDATE SCHEDULED TASKS | |
Write-SectionHeader "WINDOWS UPDATE SCHEDULED TASKS" | |
try { | |
$updateTasks = Get-ScheduledTask | Where-Object {$_.TaskPath -like "*WindowsUpdate*"} | | |
Select-Object TaskName, State, LastRunTime, NextRunTime | |
$updateTasks | Format-Table -AutoSize | Out-String | Write-InfoOutput | |
} catch { | |
Write-InfoOutput "Error retrieving Windows Update scheduled tasks: $_" | |
} | |
# MISSING SECURITY UPDATES | |
if ($PSWUAvailable) { | |
Write-SectionHeader "MISSING SECURITY UPDATES" | |
try { | |
$securityUpdates = Get-WUList -MicrosoftUpdate -Category "Security Updates" -ErrorAction SilentlyContinue | |
if ($securityUpdates) { | |
$securityUpdates | Format-Table -AutoSize | Out-String | Write-InfoOutput | |
Write-InfoOutput "Total Missing Security Updates: $($securityUpdates.Count)" | |
} else { | |
Write-InfoOutput "No missing security updates found." | |
} | |
} catch { | |
Write-InfoOutput "Error retrieving missing security updates: $_" | |
} | |
} | |
# Report completed | |
Write-Host "`nReport completed successfully!" -ForegroundColor Green | |
Write-Host "Saved to: $outputFile" -ForegroundColor Yellow | |
Write-Host "`nOpen it with: notepad '$outputFile'" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment