Skip to content

Commit f365415

Browse files
Update Maintenance-WSUS-Admin-Tool.ps1
Signed-off-by: LUIZ HAMILTON ROBERTO DA SILVA <[email protected]>
1 parent 82ed278 commit f365415

1 file changed

Lines changed: 135 additions & 49 deletions

File tree

SysAdmin-Tools/WSUS-Management-Tools/Maintenance-WSUS-Admin-Tool.ps1

Lines changed: 135 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
Luiz Hamilton Silva - @brazilianscriptguy
2424
2525
.VERSION
26-
Last Updated: 2026-02-12
26+
Last Updated: 2026-02-19
2727
Version: 3.01 (Hardened Edition)
2828
#>
2929

@@ -141,6 +141,10 @@ $script:Config = @{
141141
CsvDir = "C:\Logs-TEMP\WSUS-GUI\CSV"
142142
SettingsFile = "C:\Logs-TEMP\WSUS-GUI\settings.json"
143143

144+
# Optional: allowlist for 'Decline legacy platforms' (regex/title patterns). Empty = disabled.
145+
LegacyPlatformTitlePatterns = @()
146+
LegacyPlatformMaxToDecline = 500
147+
144148
FqdnHostname = $null
145149
LogPath = $null
146150
}
@@ -1560,8 +1564,72 @@ function Invoke-DeclineLegacyPlatforms {
15601564
[bool]$UseSSL = $false
15611565
)
15621566

1563-
Log-Message "Decline legacy platforms: option is currently not implemented in this build." "WARNING"
1564-
Log-Message "Reason: classification/product matching is environment-specific and risky without an explicit allowlist." "WARNING"
1567+
# Safety: this is environment-specific. We only act when an explicit allowlist exists.
1568+
$patterns = @()
1569+
try {
1570+
if ($Config.ContainsKey('LegacyPlatformTitlePatterns') -and $Config.LegacyPlatformTitlePatterns) {
1571+
$patterns = @($Config.LegacyPlatformTitlePatterns) | Where-Object { -not [string]::IsNullOrWhiteSpace($_) }
1572+
}
1573+
} catch { }
1574+
1575+
if (@($patterns).Count -eq 0) {
1576+
Log-Message "Decline legacy platforms: skipped (no allowlist configured in `$Config.LegacyPlatformTitlePatterns)." "WARNING"
1577+
Log-Message "Tip: add regex/title patterns like 'Windows XP|Server 2003|Itanium' to enable this step safely." "INFO"
1578+
return
1579+
}
1580+
1581+
$maxToDecline = 500
1582+
try {
1583+
if ($Config.ContainsKey('LegacyPlatformMaxToDecline') -and $Config.LegacyPlatformMaxToDecline) {
1584+
$maxToDecline = [int]$Config.LegacyPlatformMaxToDecline
1585+
}
1586+
} catch { $maxToDecline = 500 }
1587+
1588+
$wsus = Get-WSUSConnectionCached -Server $ServerName -Port $Port -UseSSL:$UseSSL
1589+
1590+
Log-Message ("Decline legacy platforms: starting. Patterns={0} Max={1}" -f ($patterns -join " ; "), $maxToDecline) "INFO"
1591+
1592+
$declined = 0
1593+
$matched = 0
1594+
1595+
try {
1596+
$scope = New-Object Microsoft.UpdateServices.Administration.UpdateScope
1597+
$scope.ApprovedStates = [Microsoft.UpdateServices.Administration.ApprovedStates]::NotApproved
1598+
$scope.IncludeDeclined = $false
1599+
1600+
$updates = $wsus.GetUpdates($scope)
1601+
1602+
foreach ($u in $updates) {
1603+
if ($script:CancelRequested) { break }
1604+
if ($declined -ge $maxToDecline) { break }
1605+
1606+
$hay = "$($u.Title)"
1607+
$isMatch = $false
1608+
foreach ($p in $patterns) {
1609+
if ($hay -match $p) { $isMatch = $true; break }
1610+
}
1611+
1612+
if (-not $isMatch) { continue }
1613+
$matched++
1614+
1615+
try {
1616+
if (-not $u.IsDeclined) {
1617+
$u.Decline()
1618+
$declined++
1619+
}
1620+
} catch {
1621+
Log-Message ("Decline legacy platforms: failed to decline update '{0}': {1}" -f $u.Title, $_.Exception.Message) "WARNING"
1622+
}
1623+
}
1624+
1625+
Log-Message ("Decline legacy platforms: matched={0} declined={1} (Cancel={2})" -f $matched, $declined, $script:CancelRequested) "INFO"
1626+
if ($declined -ge $maxToDecline) {
1627+
Log-Message ("Decline legacy platforms: reached cap ({0}). Increase `$Config.LegacyPlatformMaxToDecline if intentional." -f $maxToDecline) "WARNING"
1628+
}
1629+
} catch {
1630+
Log-Message ("Decline legacy platforms: failed: {0}" -f $_.Exception.Message) "ERROR"
1631+
throw
1632+
}
15651633
}
15661634

15671635
function Import-WsusAdminApi {
@@ -2640,12 +2708,7 @@ function Invoke-RunFromUi {
26402708

26412709
# --- Determine tasks from UI ---
26422710
$tasks = @()
2643-
2644-
2645-
# --- Determine tasks from UI ---
2646-
$tasks = @()
2647-
2648-
# Use helpers to avoid StrictMode crashes when controls/vars differ between builds.
2711+
# Use helpers to avoid StrictMode crashes when controls/vars differ between builds.
26492712
$wizUnused = (Get-CheckboxValue -Name 'chkUnusedUpdates' -Default $false) -or (Get-CheckboxValue -Name 'chkCleanupObsoleteUpdates' -Default $false)
26502713
$wizObsoletePC = (Get-CheckboxValue -Name 'chkObsoleteComputers' -Default $false) -or (Get-CheckboxValue -Name 'chkCleanupObsoleteComputers' -Default $false)
26512714
$wizUnneeded = (Get-CheckboxValue -Name 'chkUnneededFiles' -Default $false) -or (Get-CheckboxValue -Name 'chkCleanupUnneededContentFiles' -Default $false)
@@ -2719,50 +2782,18 @@ function Invoke-RunFromUi {
27192782
$UseSSL = $false
27202783
try { $UseSSL = [bool]$chkUseSsl.Checked } catch { $UseSSL = $false }
27212784

2722-
# --- Services/pool once per run ---
2723-
Ensure-WsusServicesAndPool -Server $Server
2724-
2725-
# --- Pre-warm WSUS connection cache once per run ---
2726-
$null = Get-WSUSConnectionCached -Server $Server -Port $Port -UseSSL:$UseSSL
2727-
2728-
# --- Split tasks ---
2785+
# --- Split tasks / apply Scope filter ---
27292786
$sqlTaskNames = @("CheckDB","CheckFragmentation","Reindex","ShrinkDB","BackupDB")
2730-
$sqlTasks = $tasks | Where-Object { $_ -in $sqlTaskNames }
2731-
$wsusTasks = $tasks | Where-Object { $_ -notin $sqlTaskNames }
2732-
2733-
# --- WSUS tasks ---
2734-
foreach ($t in $wsusTasks) {
2735-
if ($script:CancelRequested) { break }
2736-
2737-
switch ($t) {
2738-
"DeclineUnapproved" {
2739-
Decline-WSUSUnapproved -ServerName $Server -Port $Port -UseSSL:$UseSSL `
2740-
-OlderThanDays (Get-NumericUpDownValue -Name 'nudDeclineUnapprovedDays' -Default 30)
2741-
}
2787+
$sqlTasksAll = @($tasks | Where-Object { $_ -in $sqlTaskNames })
2788+
$wsusTasksAll = @($tasks | Where-Object { $_ -notin $sqlTaskNames })
27422789

2743-
"WsusCleanupWizard" {
2744-
Run-WsusCleanupWizard `
2745-
-IncludeUnusedUpdates:$wizUnused `
2746-
-IncludeObsoleteComputers:$wizObsoletePC `
2747-
-IncludeUnneededFiles:$wizUnneeded `
2748-
-IncludeExpiredUpdates:$wizExpired `
2749-
-IncludeSupersededUpdates:$wizSuperseded `
2750-
-AttemptCompress:$wizCompress `
2751-
-ServerName $Server -Port $Port -UseSSL:$UseSSL
2752-
}
2753-
"CompressRevisions" {
2754-
# Separate task kept for backwards-compatibility; wizard can also trigger compress.
2755-
Invoke-WsusCompressRevisionsHardened -ServerName $Server -Port $Port -UseSSL:$UseSSL
2756-
}
2757-
"DeclineLegacyPlatforms" {
2758-
Invoke-DeclineLegacyPlatforms -ServerName $Server -Port $Port -UseSSL:$UseSSL
2759-
}
2760-
2761-
2762-
}
2790+
switch ($Scope) {
2791+
'SqlOnly' { $sqlTasks = $sqlTasksAll; $wsusTasks = @() }
2792+
'WsusOnly' { $sqlTasks = @(); $wsusTasks = $wsusTasksAll }
2793+
default { $sqlTasks = $sqlTasksAll; $wsusTasks = $wsusTasksAll }
27632794
}
27642795

2765-
# --- SQL tasks (WID/SUSDB) ---
2796+
# --- 1) SQL tasks FIRST (reduces timeouts during WSUS cleanup on large SUSDB) ---
27662797
if (@($sqlTasks).Count -gt 0 -and -not $script:CancelRequested) {
27672798

27682799
$doCheckDB = ($sqlTasks -contains "CheckDB")
@@ -2771,12 +2802,67 @@ function Invoke-RunFromUi {
27712802
$doShrink = ($sqlTasks -contains "ShrinkDB")
27722803
$doBackup = ($sqlTasks -contains "BackupDB")
27732804

2805+
Log-Message ("SQL maintenance starting (first): CheckDB={0} Frag={1} Reindex={2} Shrink={3} Backup={4}" -f `
2806+
$doCheckDB,$doFrag,$doReindex,$doShrink,$doBackup
2807+
) "INFO"
2808+
27742809
Run-WIDMaintenance `
27752810
-DoCheckDB:$doCheckDB `
27762811
-DoCheckFragmentation:$doFrag `
27772812
-DoReindex:$doReindex `
27782813
-DoShrink:$doShrink `
27792814
-DoBackup:$doBackup
2815+
2816+
Log-Message "SQL maintenance finished." "INFO"
2817+
}
2818+
2819+
# --- 2) WSUS tasks SECOND (connect once; reuse cache) ---
2820+
if (@($wsusTasks).Count -gt 0 -and -not $script:CancelRequested) {
2821+
2822+
# Services/pool once per run (WSUS-only tasks)
2823+
Ensure-WsusServicesAndPool -Server $Server
2824+
2825+
# Pre-warm WSUS connection cache once per run
2826+
$null = Get-WSUSConnectionCached -Server $Server -Port $Port -UseSSL:$UseSSL
2827+
2828+
# Enforce a deterministic WSUS pipeline:
2829+
# 1) Cleanup wizard
2830+
# 2) Compress (if explicitly selected)
2831+
# 3) Legacy platform declines (if selected)
2832+
# 4) Decline unapproved LAST
2833+
$wsusOrder = @("WsusCleanupWizard","CompressRevisions","DeclineLegacyPlatforms","DeclineUnapproved")
2834+
2835+
foreach ($t in ($wsusOrder | Where-Object { $wsusTasks -contains $_ })) {
2836+
if ($script:CancelRequested) { break }
2837+
2838+
switch ($t) {
2839+
2840+
"WsusCleanupWizard" {
2841+
Run-WsusCleanupWizard `
2842+
-IncludeUnusedUpdates:$wizUnused `
2843+
-IncludeObsoleteComputers:$wizObsoletePC `
2844+
-IncludeUnneededFiles:$wizUnneeded `
2845+
-IncludeExpiredUpdates:$wizExpired `
2846+
-IncludeSupersededUpdates:$wizSuperseded `
2847+
-AttemptCompress:$wizCompress `
2848+
-ServerName $Server -Port $Port -UseSSL:$UseSSL
2849+
}
2850+
2851+
"CompressRevisions" {
2852+
# Separate task kept for backwards-compatibility; wizard can also trigger compress.
2853+
Invoke-WsusCompressRevisionsHardened -ServerName $Server -Port $Port -UseSSL:$UseSSL
2854+
}
2855+
2856+
"DeclineLegacyPlatforms" {
2857+
Invoke-DeclineLegacyPlatforms -ServerName $Server -Port $Port -UseSSL:$UseSSL
2858+
}
2859+
2860+
"DeclineUnapproved" {
2861+
Decline-WSUSUnapproved -ServerName $Server -Port $Port -UseSSL:$UseSSL `
2862+
-OlderThanDays (Get-NumericUpDownValue -Name 'nudDeclineUnapprovedDays' -Default 30)
2863+
}
2864+
}
2865+
}
27802866
}
27812867

27822868
Log-Message "Run finished." "INFO"

0 commit comments

Comments
 (0)