#Requires -Version 3.0 ################################ # Clean-WSUS # # Version 5.0 # # # # The last WSUS Script you # # will ever need! # ################################ <# ################################ # Prerequisites # ################################ 1. Ensure the script is saved in ANSI format. 2. Run the script on the WSUS Server. 3. Required Tools: SQLCMD, SSMS, and PowerShell 3.0+. 4. First Run Command: PowerShell.exe -ExecutionPolicy Bypass -File .\Clean-WSUS.ps1 -FirstRun 5. AdamJ aka Adam Marshall sucks. ################################ # Quick Overview of Features # ################################ - Remove obsolete updates - Compress update revisions - Clean synchronization logs - Optimize SUSDB - Automate maintenance tasks ################################ # Version History # ################################ Version 5.0: - Refactored script with modern PowerShell features. - Modularized cleanup logic and task management. - Improved error handling and reporting. - Enhanced compatibility with PowerShell Core/7.0+. #> param ( [Switch]$FirstRun, [Switch]$ScheduledRun ) # Global Configuration $wsusConfig = @{ ServerName = (Get-CimInstance -ClassName Win32_ComputerSystem).DNSHostName UseSSL = $false Port = 8530 } $sqlConfig = @{ ServerName = "np:\\.\pipe\MICROSOFT##WID\tsql\query" } $cleanupConfig = @{ SynchronizationLogsRetentionDays = 14 } $emailConfig = @{ From = 'WSUS@domain.com' To = 'admin@domain.com' Subject = 'WSUS Cleanup Results' SMTPServer = 'mail.domain.com' SMTPPort = 25 UseSSL = $false Credentials = $null } $reportConfig = @{ SaveReport = $true ReportPath = ".\Reports" Format = 'HTML' } $taskConfig = @{ InstallTask = $true TaskTime = "08:00AM" } ################################ # Helper Functions # ################################ Function Handle-Error { param ( [string]$ErrorMessage, [object]$Exception ) Write-Error "$ErrorMessage: $($Exception.Message)" Write-Log "ERROR: $ErrorMessage - Exception: $($Exception.Message)" -Level 'Error' Exit 1 } Function Write-Log { param ( [string]$Message, [ValidateSet('Info', 'Warning', 'Error')]$Level = 'Info' ) $timestamp = Get-Date -Format 'yyyy-MM-dd HH:mm:ss' $formattedMessage = "[$Level] $timestamp: $Message" switch ($Level) { 'Info' { Write-Host $formattedMessage -ForegroundColor Green } 'Warning' { Write-Warning $formattedMessage } 'Error' { Write-Error $formattedMessage } } if ($global:EnableLogging -eq $true) { $logFile = Join-Path -Path ".\Logs" -ChildPath "$(Get-Date -Format 'yyyy-MM-dd').log" $formattedMessage | Out-File -Append -FilePath $logFile -Encoding UTF8 } } Function Connect-WSUS { param ( [string]$ServerName, [int]$Port, [bool]$UseSSL ) Write-Log "Connecting to WSUS Server $ServerName on Port $Port (SSL: $UseSSL)" -Level 'Info' Try { [void][reflection.assembly]::LoadWithPartialName("Microsoft.UpdateServices.Administration") return [Microsoft.UpdateServices.Administration.AdminProxy]::GetUpdateServer($ServerName, $UseSSL, $Port) } Catch { Handle-Error "Failed to connect to WSUS server" $_ } } Function Execute-SQLCommand { param ( [string]$ServerInstance, [string]$Command ) Try { sqlcmd -S $ServerInstance -Q $Command -ErrorAction Stop } Catch { Handle-Error "SQL Command Execution Failed" $_ } } Function Remove-ObsoleteUpdates { param ( [Microsoft.UpdateServices.Administration.IUpdateServer]$WSUSServer ) Write-Log "Removing obsolete updates." -Level 'Info' $obsoleteUpdates = $WSUSServer.GetUpdates() | Where-Object { $_.IsObsolete } foreach ($update in $obsoleteUpdates) { $update.Delete() Write-Log "Removed obsolete update: $($update.Title)" -Level 'Info' } } Function Compress-UpdateRevisions { param ( [Microsoft.UpdateServices.Administration.IUpdateServer]$WSUSServer ) Write-Log "Compressing update revisions." -Level 'Info' $WSUSServer.GetUpdateCategories() | ForEach-Object { $_.CompressUpdateRevisions() } } Function Remove-SyncLogs { param ( [Microsoft.UpdateServices.Administration.IUpdateServer]$WSUSServer, [int]$RetentionDays ) Write-Log "Removing synchronization logs older than $RetentionDays days." -Level 'Info' $syncLogs = $WSUSServer.GetSynchronizationLogs() | Where-Object { $_.LastSyncTime -lt (Get-Date).AddDays(-$RetentionDays) } foreach ($log in $syncLogs) { $log.Delete() Write-Log "Removed sync log from: $($log.LastSyncTime)" -Level 'Info' } } Function Optimize-SUSDBIndexes { param ( [string]$SQLServer ) $query = @" USE SUSDB GO ALTER INDEX ALL ON [TableName] REBUILD; "@ Execute-SQLCommand -ServerInstance $SQLServer -Command $query Write-Log "SUSDB index optimization completed successfully." -Level 'Info' } Function Install-ScheduledTask { if ($taskConfig.InstallTask) { Write-Log "Installing the scheduled task for WSUS maintenance." -Level 'Info' Manage-ScheduledTask -TaskName "Clean-WSUS" ` -ScriptPath (Get-Location | Join-Path -ChildPath "Clean-WSUS.ps1") ` -TriggerTime $taskConfig.TaskTime } } ################################ # Main Workflow # ################################ Write-Log "Starting WSUS Cleanup Script..." -Level 'Info' # Step 1: Connect to WSUS Server $wsusServerConnection = Connect-WSUS -ServerName $wsusConfig.ServerName -Port $wsusConfig.Port -UseSSL $wsusConfig.UseSSL # Step 2: Perform Cleanup Remove-ObsoleteUpdates -WSUSServer $wsusServerConnection Compress-UpdateRevisions -WSUSServer $wsusServerConnection Remove-SyncLogs -WSUSServer $wsusServerConnection -RetentionDays $cleanupConfig.SynchronizationLogsRetentionDays Optimize-SUSDBIndexes -SQLServer $sqlConfig.ServerName # Step 3: Install Scheduled Task (if required) Install-ScheduledTask # Step 4: Generate Final Report $finalLog = "WSUS Cleanup completed successfully on $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')." Generate-Report -Message $finalLog -EmailConfig $emailConfig -ReportConfig $reportConfig Write-Log "WSUS Cleanup Script completed successfully!" -Level 'Info'