Skip to content

Instantly share code, notes, and snippets.

@greenido
Created April 29, 2025 01:03
Show Gist options
  • Save greenido/ece355aeb54583957f45a502d18e6e4b to your computer and use it in GitHub Desktop.
Save greenido/ece355aeb54583957f45a502d18e6e4b to your computer and use it in GitHub Desktop.
Windows Patch Management Information Script # Refined for improved structure, efficiency, and consistency
#########################################################
# 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