Um besser zu steuern welche Updates auf einem WSUS-Server liegen bzw. wieder gelöscht werden sollen und dies natürlich in Abhängigkeit welche Updates von Clients benötigt werden, bietet sich PowerShell an. Auf der GUI lassen sich diese Einstellungen leider nicht sehr detailliert realisieren.
Folgendes Script übernimmt die Synchronisierung und das Approvment aller Patches eines WSUS-Servers. Da nur benötigte Patches heruntergeladen werden, ist dies unproblematisch.
Die WSUS-Datenbank wächst dadurch jedoch auf 7,5 GB
Je nach Patch-Status der Clients werden initial unter 10 GB geladen und man kann sich sicher sein, dass alle Patches die durch WSUS bereitgestellt werden, im Bedarfsfall auch ausgerollt werden!
- Der Speicher des IIS-AppPools wird angepasst.
- Falls der AppPool gecrasht ist, wird er neu gestartet.
- Abgelaufene und nicht mehr benötigte Patches werden vom Server entfernt.
- Neue Kategorien und Produkte werden automatisch aktiviert.
- Unnötige Sprachpakete oder Patches für Itanium-CPUs erden abgelehnt.
Auf dem WSUS-Server sollten initial alle Produkte und Kategorien angewählt werden. Das automatische Approve muss deaktiviert bleiben bzw. sollte man alle Regeln einfach löschen.
Die gewünschten Kategorien für Zielgruppen werden in Zeile 76 und 77 eingestellt. Für weitere Zielgruppen einfach eine der beiden Zeilen kopieren und anpassen.
Hier müssen natürlich die Zielgruppen-Namen angepasst werden bzw. weitere hinzugefügt werden…
Die Zeilen 90 bis 92 definieren die benötigten eMail-Parameter. Das muss natürlich auch angepasst werden.
param( [switch]$NoSync, [switch]$NoMail, [switch]$Whatif, [switch]$Debug ) #-------------------------------------------------------------------------------------------------- # Write LOG #-------------------------------------------------------------------------------------------------- function write-log { param ( [string]$String, [switch]$SizeCheck, [switch]$Err, [switch]$NoPrefix ) $LogHistory=5 $ErrCount = $Error.count $LogFile = Join-Path -Path $PSScriptRoot -ChildPath "LogFile.log" if ( $SizeCheck ) { $LF = Get-Item -Path $LogFile -ErrorAction silentlycontinue if ( $LF.Length -gt 100KB) { if ( $LogHistory ) { for($i=$LogHistory - 1; $i -ge 1; $i--) { if ( test-path $($LF.FullName + "." + $i.tostring() ) ) { Move-Item -Path $($LF.FullName + "." + $i.tostring() ) -Destination $($LF.FullName + "." + $($i + 1).tostring() ) -force } } } Move-Item -Path $LF.FullName -Destination $($LF.FullName + ".1" ) -force write-log "Moved LogFile $LogFile to $($LF.FullName + ".1" ) because of size: $($LF.Length)" } } $Datum = get-date -Format G if ( -not $Err) { if ( -not $NoPrefix ) { $Prefix = "`($($myInvocation.ScriptName.Split('\')[-1])`[$($MyInvocation.ScriptLineNumber)`]`)" $TMP = "$($Datum) $($Prefix): $String" } else { $TMP = $String } } else { if ( $String ) { write-log $String } foreach ( $E in $Error ) { $Prefix = "`($($myInvocation.ScriptName.Split('\')[-1])`[$($E.InvocationInfo.ScriptLineNumber) '/ $($E.InvocationInfo.OffsetInLine)`]`)" $TMP = "$($Datum) $($Prefix): $($E.Exception.Message)" write-log -String $TMP -NoPrefix } } if ( $Debug ) { $TMP } $TMP | Out-File -append -encoding UTF8 -FilePath $LogFile -ErrorAction silentlycontinue if ($ErrCount -eq 0) { $Error.Clear() } } #-------------------------------------------------------------------------------------------------- write-log "---------- Start Script: Manage-WSUS --------------------------------------------------------------" -SizeCheck Import-Module WebAdministration if ( $Error ){ write-log "Error loading Module" -err exit } $Approves = @([pscustomobject]@{GroupName = "Server"; Classification=@("Wichtige Updates", "Definitionsupdates", "Definition Updates", "Sicherheitsupdates", "Update-Rollups", "Updates", "Treiber", "Treibersätze")}) $Approves += @([pscustomobject]@{GroupName = "Workstation"; Classification=@("Wichtige Updates", "Definitionsupdates", "Definition Updates", "Sicherheitsupdates", "Update-Rollups", "Updates", "Treiber", "Treibersätze", "Feature Packs", "Service Packs", "Tools", "Upgrades")}) #Definitionsupdates #Feature Packs #Service Packs #Sicherheitsupdates #Tools #Treiber #Update-Rollups #Updates #Upgrades #Wichtige Updates $serverAdminEmail = "admins@powershell.pub" $fromEmail = $env:computername + "@powershell.pub" $smtpServer = "mail.powershell.pub" write-log "Get AppPool private Memory..." $tmp = Get-ChildItem IIS:\AppPools | Where-Object { $_.Name -eq "WsusPool" } | ForEach-Object { Get-ItemProperty $_.PSPath -Name recycling.periodicrestart.privateMemory } if ( $tmp.value -lt 3145728 -or $tmp.value -gt 4194304 ) { write-log "Change AppPool private Memory to 3MB" Get-ChildItem IIS:\AppPools | Where-Object { $_.Name -eq "WsusPool" } | ForEach-Object { Set-ItemProperty $_.PSPath -Name recycling.periodicrestart.privateMemory -Value 3145728 } } write-log "Check AppPool running state..." $tmp = Get-WebAppPoolState WsusPool if ( $tmp.value -ne "Started") { write-log "AppPool ist stopped! Starting AppPool." Start-WebAppPool WsusPool } write-log "Get-WSUSServer" $SRVs = Get-WsusServer foreach ( $SRV in $SRVs ) { # Start Syncronisation if ( $NoSync ) { write-log "No Sync" } else { write-log "Sync WSUS: $($SRV.Name)" ($SRV.GetSubscription()).StartSynchronization() # Wait for Synchronization $State = 0.0 while (($SRV.GetSubscription()).GetSynchronizationStatus() -eq "Running" ) { $P = ($SRV.GetSubscription()).GetSynchronizationProgress() if ( $P.TotalItems -eq 0 ) { $tmp = 0 } else { $tmp = $P.ProcessedItems / $P.TotalItems } if ( $tmp -ge $State ) { write-log "Sync-Progress: $([int]($tmp * 100))%" $State += 0.2 } sleep -Seconds 10 } write-log "Sync done on $($SRV.Name)" } write-log "Enable all Products and Classes" Get-WSUSProduct -UpdateServer $SRV | Set-WSUSProduct Get-WSUSClassification -UpdateServer $SRV | Set-WSUSClassification # Get all Availabe Updates write-log "Get all Approved Updates" $denied = 0 $approved = 0 $Updates = Get-WsusUpdate -UpdateServer $Srv -Approval Approved write-log "Decline superseded Updates" foreach ( $Update in $Updates ) { if (($Update.Update.Title -match “ia64|itanium”) -or ($Update.Update.IsSuperseded -eq $true) -or ($Update.Update.Title -match "LanguageFeatureOnDemand|Sprachpaket|LanguagePack|LanguageInterfacePack" )) #-and $Update.Update.Title -notmatch “de-DE|en-US”) ) { write-log "Deny Update: (KB $($Update.Update.KnowledgebaseArticles[0])) $($Update.Update.Title)" $Update | Deny-WsusUpdate -whatif:$Whatif $denied ++ } } $Updates = Get-WsusUpdate -UpdateServer $Srv -Approval AnyExceptDeclined -Status FailedorNeeded write-log "Approve needed Updates" $license = @() foreach ( $Update in $Updates ) { if (($Update.Update.Title -match “ia64|itanium”) -or ($Update.Update.IsSuperseded -eq $true) -or (($Update.Update.Title -match "LanguageFeatureOnDemand|Sprachpaket|LanguagePack|LanguageInterfacePack" ) -and $Update.Update.Title -notmatch "Antivirus")) #-and $Update.Update.Title -notmatch “de-DE|en-US”) ) { write-log "Deny Update: (KB $($Update.Update.KnowledgebaseArticles[0])) $($Update.Update.Title)" $Update | Deny-WsusUpdate -whatif:$Whatif $denied ++ } else { if ( $Update.ComputersNeedingThisUpdate -gt 0 ) { if ($update.Update.RequiresLicenseAgreementAcceptance) { write-log "License Required: (KB $($Update.Update.KnowledgebaseArticles[0])) $($Update.Update.Title)" $license += $update.Update.Title } else { foreach ( $Approve in $Approves ) { if ( $Approve.Classification -contains $Update.Classification ) { write-log "Approve Update for $($Approve.GroupName): (KB $($Update.Update.KnowledgebaseArticles[0])) $($Update.Update.Title)" $Update | Approve-WsusUpdate -Action Install -TargetGroupName $Approve.GroupName -whatif:$Whatif $approved ++ } } } } } } } write-log "Updates Denied : $denied" write-log "Updates Approved: $approved" if ( $denied -gt 0 ) { write-log "Cleanup WSUS" $SRVS | Invoke-WsusServerCleanup -CleanupUnneededContentFiles -DeclineExpiredUpdates -confirm:$false -whatif:$Whatif } if ($license.Count -gt 0 -and -not $NoMail) { write-log "Send Mail for Licences" $txt = "Hallo lieber Admin,`nes müssen Lizenzen auf dem WSUS Server ({0}) abgenickt werden. Im Einzelnen sind das:`n`n" -f [System.Net.Dns]::GetHostByName(($env:computerName)).HostName $license | foreach { $txt += "* {0}`n" -f $_ } Send-MailMessage -SmtpServer $smtpServer -Subject 'WSUS Lizenzen zum freigeben' -To $serverAdminEmail -Body $txt -From $fromEmail -Encoding ([Text.Encoding]::UTF8) } write-log "---------- Script done: Manage-WSUS ----------------------------------------------------------------"