Menü Schließen

WSUS Server aufräumen und bereinigen

WSUS 3.0 Logo

Die Windows Server Update Services, kurz WSUS, sollte man von Zeit zu Zeit warten und nicht benötigte Updates entfernen. Das geht zum einen recht gut mit dem “Assistent für die Serverbereinigung”, jedoch muss man auch Hand anlegen und sollte zum Beispiel die Updates entfernen die von anderen ersetzt wurden (Superseded Updates). Und das erkläre ich nachfolgend.

WSUS aufräumen von Superseded Updates oder in deutsch die von anderen Updates ersetzt wurden

Die Frage vorweg – Warum sollte man das machen? Der Grund ist recht einfach um den Speicher auf der Festplatte des WSUS möglichst schlank zu halten und um ggf. Fehler wie “WARNING: Exceeded max server round trips: 0x80244010” auf dem Windowsclient  zu vermeiden.

  1. WSUS Konsole starten
  2. in die Ansicht Updates -> Alle Updates wechseln
WSUS Ansicht Alle Updates
WSUS Ansicht Alle Updates
  • den Filter wie folgt einstellen:
  • WSUS Filter Settings
    WSUS Filter Settings
    1. Genehmigung: “Alle bis auf abgelehnte”
    2. Status: “Any”
    3. und auf “Aktualisieren” klicken
  • nun ggf. noch die Spalte “Ersatz” aktivieren
  • WSUS Spalte Ersatz
    WSUS Spalte Ersatz
  • alle Updates nach der Spalte “Ersatz” sortieren
  • WSUS Symbole Ersatz

    Da sich nun alles um die Superseded Updates dreht, erkläre ich nachfolgend kurz die Symbole:

    SymbolErklärungAblehnen / Löschen
    WSUS Symbol kein Ersatz
    WSUS Symbol kein Ersatz

    Dieses Update ersetzt andere Updates nein !

    WSUS Symbol Wird und ist Ersatz
    WSUS Symbol Wird und ist Ersatz

    Dieses Update ersetzt ein Update, wird aber auch von einem anderen ersetzt. Ja

    WSUS Symbol Ersatz
    WSUS Symbol Ersatz

    Dieses Update wird von einem anderen Update ersetzt. Ja

    • Weiter geht es an dieser Stelle und wir prüfen ob die Updates, die die andere Ersetzen, auch genehmigt wurden und zur Installation freigegeben sind. Dafür schaue ich unter “Zusätzliche Details” nach dem Punkt “Updates, die dieses Update ersetzen:” Dort klicke ich auf das entsprechende Update das dieses ersetzt und schaue mir von diesem Update die “Genehmigungszusammenfassung” an. Dieses sollte also zur Installation genehmigt werden.
    • ist dies der Fall können alle Updates die Ersetzt wurden ablehnen
    WSUS Ersatz Ablehnen
    WSUS Ersatz Ablehnen
  • im letzten Schritt wird der “Assistent für die Serverbereinigung” ausgeführt
    • unter “Optionen” -> auf “Assistent für die Serverbereinigung” klicken
  • WSUS Option Assitent Bereinigung
    WSUS Option Assitent Bereinigung
  • hier nun entsprechend den eigenen Vorgaben, die entsprechenden Optionen auswählen, ich nehme alle, Beschreibung sollte selbsterklärend sein
  • WSUS Server Bereinigung Optionen
    WSUS Server Bereinigung Optionen
  • und den Assistenten starten
  • Das Resultat

    Damit wurden alle nicht mehr benötigten Windows Updates von der Festplatte gelöscht und Platz geschaffen. Auch wurden alle von Computern für diese Updates gemeldeten Ereignisse aus der Datenbank gelöscht. Dadurch müssen Clients ggf. nicht zu viele Anfragen zur Klärung des Updatestatus an den Server senden und Probleme wie den Roun Trip, werden vermieden. Wird nun ein neuer Client installiert, so wird er immer das neueste Update installieren, es hat ja eh ein altes und nun entferntes ersetzt …

    Empfehlung – Automatische Genehmigung

    Unter Optionen -> “Automatische Genehmigungen” und dort unter dem Reiter “Erweitert” empfehle ich die Optionen für die “Revisionen für Updates” zu aktivieren:

    • Neue Revisionen von bereits genehmigten Updates automatisch genehmigen
      • Updates automatisch ablehnen, wenn eine neue Revision deren Ablauf bewirkt
    WSUS Option Auto Genehmigung
    WSUS Option Auto Genehmigung

    Aufräumen mittels Powershell

    Der WSUS lässt sich auch per Powershell verwalten. So kann man mittels einfachem Befehl obiges einfach per Powershell ausführen und z.B. automatisiert in regelmäßigen Abständen durchführen. Nachfolgender Powershellbefehl führt folgendes durch:

    • entfernen aller Superseeded Updates
    • entfernen aller Expired Updates
    • entfernen alte Computer

    und los gehts:

    • auf dem WSUS anmelden
    • Powershell als Administrator öffnen und folgenden Befehl eingeben und ausführen:
    Invoke-WsusServerCleanup -DeclineSupersededUpdates -DeclineExpiredUpdates -CleanupObsoleteComputers -CleanupObsoleteUpdates

    Diesen Befehl muss man ggf. öfter hintereinander ausführen, da ansonten ein Fehler mit “..Timeout..” erfolgt, der Befehl dann aber an der Stelle wo es aufhörte, weiter macht. In meinem Fall hatte ich nach 9 maligem ausführen die Nase voll und habe diese einfache Powershell While-Schleife laufen lassen. Diese lief dann über Nacht und wurde ca. 320x ausgeführt :uff: Danach wurde mir der Erfolg angezeigt und der WSUS aufgeräumt.

    #$wsus = Get-WsusServer -Name localhost -port 8530 #Extra
    #$wsus.GetDatabaseConfiguration().CreateConnection() # Extra
    while(!($Check))
    {
        $check = $true
        try{        
            Invoke-WsusServerCleanup -DeclineSupersededUpdates -DeclineExpiredUpdates -CleanupObsoleteComputers -CleanupObsoleteUpdates        
        }
        catch{
            $check = $false
            Write-Host "Timeout"
        }
    }

    WSUS Datenbank reorganisieren

    • Windows Server 2016 mit WSUS in lokaler SQLExpress Datenbank

    Von Zeit zu Zeit ist es notwendig die WSUS Datenbank zu reorganisieren und damit die Indexes neuzuerstellen und fragementierte Tabellen aufzuräumen. Hierfür gibt es ein sehr gutes SQL-Script in der Microsoft Technet Gallery: https://gallery.technet.microsoft.com/scriptcenter/Optimize-and-cleanup-of-eb9d8640 das den Job übernimmt. Man kann es sehr gut z.B. monatlich als Task laufen lassen.

    Voraussetzung ist die Installation der Tools für SQLCMD, steht aber auch im Artikel.

    Das Script ist für Windows Server 2012 R2 und 2016 und sieht wie folgt aktuell aus, wobei ich den Schalter “SET QUOTED_IDENTIFIER ON;” bereits hinzugefügt habe:

    /****************************************************************************** 
    This sample T-SQL script performs basic maintenance tasks on SUSDB 
    1. Identifies indexes that are fragmented and defragments them. For certain 
       tables, a fill-factor is set in order to improve insert performance. 
       Based on MSDN sample at http://msdn2.microsoft.com/en-us/library/ms188917.aspx 
       and tailored for SUSDB requirements 
    2. Updates potentially out-of-date table statistics. 
    ******************************************************************************/ 
     
    USE SUSDB; 
    GO 
    SET NOCOUNT ON;
    SET QUOTED_IDENTIFIER ON; 
     
    -- Rebuild or reorganize indexes based on their fragmentation levels 
    DECLARE @work_to_do TABLE ( 
        objectid int 
        , indexid int 
        , pagedensity float 
        , fragmentation float 
        , numrows int 
    ) 
     
    DECLARE @objectid int; 
    DECLARE @indexid int; 
    DECLARE @schemaname nvarchar(130);  
    DECLARE @objectname nvarchar(130);  
    DECLARE @indexname nvarchar(130);  
    DECLARE @numrows int 
    DECLARE @density float; 
    DECLARE @fragmentation float; 
    DECLARE @command nvarchar(4000);  
    DECLARE @fillfactorset bit 
    DECLARE @numpages int 
     
    -- Select indexes that need to be defragmented based on the following 
    -- * Page density is low 
    -- * External fragmentation is high in relation to index size 
    PRINT 'Estimating fragmentation: Begin. ' + convert(nvarchar, getdate(), 121)  
    INSERT @work_to_do 
    SELECT 
        f.object_id 
        , index_id 
        , avg_page_space_used_in_percent 
        , avg_fragmentation_in_percent 
        , record_count 
    FROM  
        sys.dm_db_index_physical_stats (DB_ID(), NULL, NULL , NULL, 'SAMPLED') AS f 
    WHERE 
        (f.avg_page_space_used_in_percent < 85.0 and f.avg_page_space_used_in_percent/100.0 * page_count < page_count - 1) 
        or (f.page_count > 50 and f.avg_fragmentation_in_percent > 15.0) 
        or (f.page_count > 10 and f.avg_fragmentation_in_percent > 80.0) 
     
    PRINT 'Number of indexes to rebuild: ' + cast(@@ROWCOUNT as nvarchar(20)) 
     
    PRINT 'Estimating fragmentation: End. ' + convert(nvarchar, getdate(), 121) 
     
    SELECT @numpages = sum(ps.used_page_count) 
    FROM 
        @work_to_do AS fi 
        INNER JOIN sys.indexes AS i ON fi.objectid = i.object_id and fi.indexid = i.index_id 
        INNER JOIN sys.dm_db_partition_stats AS ps on i.object_id = ps.object_id and i.index_id = ps.index_id 
     
    -- Declare the cursor for the list of indexes to be processed. 
    DECLARE curIndexes CURSOR FOR SELECT * FROM @work_to_do 
     
    -- Open the cursor. 
    OPEN curIndexes 
     
    -- Loop through the indexes 
    WHILE (1=1) 
    BEGIN 
        FETCH NEXT FROM curIndexes 
        INTO @objectid, @indexid, @density, @fragmentation, @numrows; 
        IF @@FETCH_STATUS < 0 BREAK; 
     
        SELECT  
            @objectname = QUOTENAME(o.name) 
            , @schemaname = QUOTENAME(s.name) 
        FROM  
            sys.objects AS o 
            INNER JOIN sys.schemas as s ON s.schema_id = o.schema_id 
        WHERE  
            o.object_id = @objectid; 
     
        SELECT  
            @indexname = QUOTENAME(name) 
            , @fillfactorset = CASE fill_factor WHEN 0 THEN 0 ELSE 1 END 
        FROM  
            sys.indexes 
        WHERE 
            object_id = @objectid AND index_id = @indexid; 
     
        IF ((@density BETWEEN 75.0 AND 85.0) AND @fillfactorset = 1) OR (@fragmentation < 30.0) 
            SET @command = N'ALTER INDEX ' + @indexname + N' ON ' + @schemaname + N'.' + @objectname + N' REORGANIZE'; 
        ELSE IF @numrows >= 5000 AND @fillfactorset = 0 
            SET @command = N'ALTER INDEX ' + @indexname + N' ON ' + @schemaname + N'.' + @objectname + N' REBUILD WITH (FILLFACTOR = 90)'; 
        ELSE 
            SET @command = N'ALTER INDEX ' + @indexname + N' ON ' + @schemaname + N'.' + @objectname + N' REBUILD'; 
        PRINT convert(nvarchar, getdate(), 121) + N' Executing: ' + @command; 
        EXEC (@command); 
        PRINT convert(nvarchar, getdate(), 121) + N' Done.'; 
    END 
     
    -- Close and deallocate the cursor. 
    CLOSE curIndexes; 
    DEALLOCATE curIndexes; 
     
     
    IF EXISTS (SELECT * FROM @work_to_do) 
    BEGIN 
        PRINT 'Estimated number of pages in fragmented indexes: ' + cast(@numpages as nvarchar(20)) 
        SELECT @numpages = @numpages - sum(ps.used_page_count) 
        FROM 
            @work_to_do AS fi 
            INNER JOIN sys.indexes AS i ON fi.objectid = i.object_id and fi.indexid = i.index_id 
            INNER JOIN sys.dm_db_partition_stats AS ps on i.object_id = ps.object_id and i.index_id = ps.index_id 
     
        PRINT 'Estimated number of pages freed: ' + cast(@numpages as nvarchar(20)) 
    END 
    GO 
     
     
    --Update all statistics 
    PRINT 'Updating all statistics.' + convert(nvarchar, getdate(), 121)  
    EXEC sp_updatestats 
    PRINT 'Done updating statistics.' + convert(nvarchar, getdate(), 121)  
    GO 

    Das Script wird in eine Datei gespeichert z.B. “WsusDBMaintenance.sql”, die dann später im Task aufgerufen wird. Der SQLExpress läuft unter dem “NT Service” und ohne weitere Berechtigungen, sodass ich ihn auch nicht als Administrator in der Befehlszeile ausführen konnte. Daher ein Task mit Output in ein Debug File.

    WSUS Service SQL Logon User
    WSUS Service SQL Logon User

    Ich musste den SQLCMD Befehl aus dem obigen Artikel etwas anpassen, da er ansonsten nicht lief. Die wesentlichen Einstellungen des WSUS-Tasks:

    Das Programm / Script das ausgeführt wird: # sqlcmd -S np:\.\pipe\MSSQL$SQLEXPRESS\sql\query -i <Pfad zum WsusDBMaintenance .sql Script> -o “<Pfad zur debug.txt>”

    grafik 5
    WSUS Task Cleanup Script
    WSUS Task Cleanup Script

    Den Task dann noch den eigenen Abläufen entsprechend anpassen und gut ist.

    WSUS Datenbank verkleinern

    Ein weiterer Schritt den WSUS aufzuräumen und zu optimieren ist die Datenbank zu verkleinern / shrinken. Das geht am besten mit dem MS SQL Management Studio.

    WSUS SUSDB Shrink
    WSUS SUSDB Shrink

    Have Fun …

    21 Kommentare

    1. Lun4r

      Hi dein Artikel hat mir sehr weitergeholfen die “Logik” vom WSUS nachzuvollziehen und die nicht vollumfängliche Bereinigung nachvollziehen zu können. Danke dafür!
      Darauf aufbauend und mit ein bisschen Netzrecherche hier und ein bisschen CopyPaste da und ein bisschen Anpassung meinerseits habe ich folgendes powershellscript erstellt/modifiziert welches alle genehmigten WSUS Updates einliest dann deren “Ersetzt” Status prüft und daraufhin alle ersetzten Updates ablehnt. Danach wird die Autobereinigung vom WSUS aufgerufen (nur für die Updates). Somit werden sie auch gleich entfernt.
      Das ganze wird unter den eingegebenen Pfaden dann fortlaufend protokolliert (Müssen angepasst werden!).
      Quelle vom Grundscript: https://gallery.technet.microsoft.com/scriptcenter/Cleanup-WSUS-server-4424c9d6

      Wenn man dieses Script wie folgt koppelt muss man sich fast nicht mehr um den WSUS-Updatezyklus kümmern.
      1. Updatekatalogsynch zwischen WSUS und Windows Update in der Nacht
      2. Automatische Genehmigungsregeln für Updates im WSUS einstellen
      (hier geht bestimmt auch noch was mit Powershell wenn mans ganz feingliedrig haben möchte z.Bsp.: gewisse Office Produkte/Bestandteile nicht zu genehmigen)
      3. Das PowershellScript dann paar Stunden später automatisiert laufen lassen

      PS: ! $server, $port und die Logpaths müssen angepasst werden !

      Powershellcode:

      Start-Transcript -Path “C:\WSUS_Skript_Bereinigung\Log\2LogPowershell.txt” -Append

      $server = ”
      $port = ”
      Write-Progress -Activity ‘Getting WSUS server’
      $WSUSserver = Get-WsusServer -Name $server -PortNumber $port
      Write-Progress -Activity ‘Getting approved updates, this may take a while…’ -PercentComplete -1
      $approvedupdates = Get-WsusUpdate -UpdateServer $WSUSserver -Approval Approved -Status InstalledOrNotApplicableOrNoStatus
      Write-Progress -Activity ‘Retrieved updates’ -PercentComplete 90
      $i = 0
      $superseded = $approvedupdates | ? {$_.Update.IsSuperseded -eq $true}
      $total = $superseded.count

      $datum = Get-Date -Format g
      foreach ($update in $superseded)
      {
      $datum + ” [Abgelehnt] ” + $update.update.title + “`r`n” | Out-File C:\WSUS_Skript_Bereinigung\Log\1LogAbgelehnteUpdates.txt -Append
      }

      foreach ($update in $superseded)
      {
      Write-Progress -Activity ‘Declining updates’ -Status “$($update.Update.Title)” -PercentComplete (($i/$total) * 100)
      $update.Update.Decline()
      $i++
      }

      Write-Host “Total declined updates: $total” -ForegroundColor Yellow

      Invoke-WsusServerCleanup -DeclineSupersededUpdates -DeclineExpiredUpdates -CleanupObsoleteUpdates -cleanupUnneededContentfiles -CompressUpdates

      Zusätzlich kann man auch die Synchberichte 1mal im Monat leeren lassen da die irgendwann ewig brauchen zum laden und zwar wie folgt:
      .sql Datei mit folgendem Inhalt erstellen lautet hier “Synchronisierungsberichteloeschen.sql” :
      USE SUSDB
      GO
      DELETE FROM tbEventInstance WHERE EventNamespaceID = ‘2’ AND EVENTID IN (‘381’, ‘382’, ‘384’, ‘386’, ‘387’, ‘389’)

      Dazu dann eine batch zum automatisieren:
      sqlcmd -S np:\\.\pipe\MICROSOFT##WID\tsql\query -i C:\WSUS_Skript_Bereinigung\Synchronisierungsberichteloeschen.sql

      Grüße

      • JARVIS

        Hi,
        super das ich helfen konnte. Deine Weiterentwicklung sieht aber auch klasse aus. Leider kann ich sei grade nicht testen, aber sie hilft bestimmt den nächste …Thx

        • Lun4r

          Danke.
          Mir ist heut beim testen aufgefallen das ich noch folgendes in der letzten Zeile vom 1. Script vergessen habe und somit die nicht verwendeten Updaterevisionen auf dem WSUS liegen bleiben.
          Oben:
          Invoke-WsusServerCleanup -DeclineSupersededUpdates -DeclineExpiredUpdates -CleanupObsoleteUpdates
          Neu:
          Invoke-WsusServerCleanup -DeclineSupersededUpdates -DeclineExpiredUpdates -CleanupObsoleteUpdates -cleanupUnneededContentfiles -CompressUpdates

          Zusätzlich habe ich noch folgendes Script gefunden welches man davor laufen lassen kann um spezielle Updates rauszukehren.
          https://gallery.technet.microsoft.com/Decline-WSUS-Update-Types-13fec5e0#content

            • Lun4r

              Vielen Dank und nun kommt noch mehr 😉
              Nachdem das Script vom Technet nicht so wollte wie ich, habe ich es in das bestehende Script eingebaut.
              Für alle Produkte die man im speziellen ablehnen möchte (Sharepoint, Office 32bit Updates etc.pp.) gibt es nun einzelne Abfrageblöcke die dies erledigt.
              Somit kann man sich nach belieben eigene Regeln bauen, da in der Updatebezeichnung gesucht wird.

              Ein Block zusammengeschrieben sieht so aus wenn ich alle Itanium Updates ablehnen möchte:
              $i = 0
              $itanium = $approvedupdates | ? {$_.Update.Title -match “ia64|itanium”}
              $totalitanium = $itanium.count
              $datum = Get-Date -Format g #Wird nur einmal benötigt
              foreach ($update in $itanium)
              {
              $datum + ” [Abgelehnt] ” + ” [itanium] ” + $update.update.title + “`r`n” | Out-File C:\WSUS_Skript_Bereinigung\Log\1LogAbgelehnteUpdates.txt -Append
              }

              foreach ($update in $itanium)
              {
              Write-Progress -Activity ‘Declining updates’ -Status “$($update.Update.Title)” -PercentComplete (($i/$totalitanium) * 100)
              $update.Update.Decline()
              $i++
              }
              Write-Host “Total declined itanium updates: $totalitanium” -ForegroundColor Yellow

              Produkte welche man nicht ablehnen möchte muss man einfach mit “” auskommentieren im Script (ich habe das als Beispiel für die Office 32 und 64 bit Updates getan).

              Der folgende Block ersetzt im Eingangsscript alles ab der Zeile 11 also nach:
              Write-Progress -Activity ‘Retrieved updates’ -PercentComplete 90

              Script:

              $i = 0
              $superseded = $approvedupdates | ? {$_.Update.IsSuperseded -eq $true}
              $itanium = $approvedupdates | ? {$_.Update.Title -match “ia64|itanium”}
              $Sharepoint = $approvedupdates | ? {$_.Update.Title -match “SharePoint Enterprise Server|SharePoint Foundation|SharePoint Server|FAST Search Server”}
              $Win10x86 = $approvedupdates | ? {$_.Update.Title -match “Windows 10” -and $_.Update.Title -match “x86”}
              $Win10arm64 = $approvedupdates | ? {$_.Update.Title -match “Windows 10” -and $_.Update.Title -match “arm64”}
              $Officex86 = $approvedupdates | ? {$_.Update.Title -match “Microsoft Office|Microsoft Access|Microsoft Excel|Microsoft Outlook|Microsoft Onenote|Microsoft PowerPoint|Microsoft Publisher|Microsoft Word” -and $_.Update.Title -match “32-Bit”}
              $Officex64 = $approvedupdates | ? {$_.Update.Title -match “Microsoft Office|Microsoft Access|Microsoft Excel|Microsoft Outlook|Microsoft Onenote|Microsoft PowerPoint|Microsoft Publisher|Microsoft Word” -and $_.Update.Title -match “64-Bit”}

              $totalsuperseded = $superseded.count
              $totalitanium = $itanium.count
              $totalSharepoint = $Sharepoint.count
              $totalWin10x86 = $Win10x86.count
              $totalWin10arm64 = $Win10arm64.count
              $totalOfficex86 = $Officex86.count
              $totalOfficex64 = $Officex64.count

              $datum = Get-Date -Format g

              foreach ($update in $superseded)
              {
              $datum + ” [Abgelehnt] ” + ” [Superseded] ” + $update.update.title + “`r`n” | Out-File C:\WSUS_Skript_Bereinigung\Log\1LogAbgelehnteUpdates.txt -Append
              }

              foreach ($update in $superseded)
              {
              Write-Progress -Activity ‘Declining updates’ -Status “$($update.Update.Title)” -PercentComplete (($i/$totalsuperseded) * 100)
              $update.Update.Decline()
              $i++
              }

              $i = 0

              foreach ($update in $itanium)
              {
              $datum + ” [Abgelehnt] ” + ” [itanium] ” + $update.update.title + “`r`n” | Out-File C:\WSUS_Skript_Bereinigung\Log\1LogAbgelehnteUpdates.txt -Append
              }

              foreach ($update in $itanium)
              {
              Write-Progress -Activity ‘Declining updates’ -Status “$($update.Update.Title)” -PercentComplete (($i/$totalitanium) * 100)
              $update.Update.Decline()
              $i++
              }

              $i = 0

              foreach ($update in $Sharepoint)
              {
              $datum + ” [Abgelehnt] ” + ” [Sharepoint] ” + $update.update.title + “`r`n” | Out-File C:\WSUS_Skript_Bereinigung\Log\1LogAbgelehnteUpdates.txt -Append
              }

              foreach ($update in $Sharepoint)
              {
              Write-Progress -Activity ‘Declining updates’ -Status “$($update.Update.Title)” -PercentComplete (($i/$totalSharepoint) * 100)
              $update.Update.Decline()
              $i++
              }
              $i = 0

              foreach ($update in $Win10x86)
              {
              $datum + ” [Abgelehnt] ” + ” [Win10x86] ” + $update.update.title + “`r`n” | Out-File C:\WSUS_Skript_Bereinigung\Log\1LogAbgelehnteUpdates.txt -Append
              }

              foreach ($update in $Win10x86)
              {
              Write-Progress -Activity ‘Declining updates’ -Status “$($update.Update.Title)” -PercentComplete (($i/$totalWin10x86) * 100)
              $update.Update.Decline()
              $i++
              }

              $i = 0

              foreach ($update in $Win10arm64)
              {
              $datum + ” [Abgelehnt] ” + ” [Win10arm64] ” + $update.update.title + “`r`n” | Out-File C:\WSUS_Skript_Bereinigung\Log\1LogAbgelehnteUpdates.txt -Append
              }

              foreach ($update in $Win10arm64)
              {
              Write-Progress -Activity ‘Declining updates’ -Status “$($update.Update.Title)” -PercentComplete (($i/$totalWin10arm64) * 100)
              $update.Update.Decline()
              $i++
              }

              Write-Host “Total declined superseeded updates: $totalsuperseded” -ForegroundColor Yellow
              Write-Host “Total declined itanium updates: $totalitanium” -ForegroundColor Yellow
              Write-Host “Total declined sharepoint updates: $totalSharepoint” -ForegroundColor Yellow
              Write-Host “Total declined Windows 10 32bit updates: $totalWin10x86” -ForegroundColor Yellow
              Write-Host “Total declined Windows 10 arm64 updates: $totalWin10arm64” -ForegroundColor Yellow
              Write-Host “Total declined Office 32bit updates: $totalOfficex86” -ForegroundColor Yellow
              Write-Host “Total declined Office 64bit updates: $totalOfficex64” -ForegroundColor Yellow

              Invoke-WsusServerCleanup -DeclineSupersededUpdates -DeclineExpiredUpdates -CleanupObsoleteUpdates -CleanupUnneededContentFiles -CompressUpdates

              Grüße =)

            • Lun4r

              Irgendwie hat er in dem Posting im Skript die Auskommentierung tatsächlich auskommentiert und verschwinden lassen 😀

              Der auskommentierte Officeblock mit ist komplett weg. 🙂

            • Lun4r

              Hier nun nochmal das komplette Skript ohne Auskommentierung (unten).
              Erklärung:
              Damit sollte es dann auch Final sein. Wie schon weiter oben beschrieben kann man sich dann auch noch eigene Filter zum ablehnen bauen welche auf den Namen des Updates zielen.
              Aktuell werden im Script ablehent:
              Alle ersetzten Updates
              itanium Updates
              Sharepoint Updates
              Windows 10 32bit
              Windows 10 ARM64
              Office 32bit
              Office 64bit

              ! Hier müssen die nicht gewünschten Schleifen einfach auskommentiert werden. !
              Zum Beispiel. man hat nur Office 64bit Installationen und möchte folglich diese Update behalten aber nicht die 32bit dann kommentiert man folgende 3 Zeilen/Blöcke aus:
              1.
              $Officex64 = $approvedupdates | ? {$_.Update.Title -match “Microsoft Office|Microsoft Access|Microsoft Excel|Microsoft Outlook|Microsoft Onenote|Microsoft PowerPoint|Microsoft Publisher|Microsoft Word” -and $_.Update.Title -match “64-Bit”}
              2.
              $i = 0

              foreach ($update in $Officex64)
              {
              $datum + ” [Abgelehnt] ” + ” [Officex64] ” + $update.update.title + “`r`n” | Out-File C:\WSUS_Skript_Bereinigung\Log\1LogAbgelehnteUpdates.txt -Append
              }

              foreach ($update in $Officex64)
              {
              Write-Progress -Activity ‘Declining updates’ -Status “$($update.Update.Title)” -PercentComplete (($i/$totalOfficex64) * 100)
              $update.Update.Decline()
              $i++
              }
              3.
              Write-Host “Total declined Office 64bit updates: $totalOfficex64” -ForegroundColor Yellow

              Der “Start-Transcript -Path” und “Out-File”-Paths in jeder Logschleife (hier war ich zu faul für ne Variable, CopyPaste ging schneller 😉 ) können individualisiert werden und der WSUS Servername ($server) muss geändert werden.
              Portnummer ($port) sollte eigentlich bei den meisten passen (Standardport).

              Viel Spaß damit und bleibt gesund. =)
              PS: Verbesserungsvorschläge und Fragen sind gern gelesen 😉

              ………………………………………………………………………………………………………………………………………
              Skript
              ………………………………………………………………………………………………………………………………………
              Start-Transcript -Path “C:\WSUS_Skript_Bereinigung\Log\2LogPowershell.txt” -Append

              $server = ‘[WSUSName].[Domain]’
              $port = ‘8530’

              Write-Progress -Activity ‘Getting WSUS server’
              $WSUSserver = Get-WsusServer -Name $server -PortNumber $port
              Write-Progress -Activity ‘Getting approved updates, this may take a while…’ -PercentComplete -1
              $approvedupdates = Get-WsusUpdate -UpdateServer $WSUSserver -Approval Approved -Status InstalledOrNotApplicableOrNoStatus
              Write-Progress -Activity ‘Retrieved updates’ -PercentComplete 90

              $i = 0
              $superseded = $approvedupdates | ? {$_.Update.IsSuperseded -eq $true}
              $itanium = $approvedupdates | ? {$_.Update.Title -match “ia64|itanium”}
              $Sharepoint = $approvedupdates | ? {$_.Update.Title -match “SharePoint Enterprise Server|SharePoint Foundation|SharePoint Server|FAST Search Server”}
              $Win10x86 = $approvedupdates | ? {$_.Update.Title -match “Windows 10” -and $_.Update.Title -match “x86”}
              $Win10arm64 = $approvedupdates | ? {$_.Update.Title -match “Windows 10” -and $_.Update.Title -match “arm64”}
              $Officex86 = $approvedupdates | ? {$_.Update.Title -match “Microsoft Office|Microsoft Access|Microsoft Excel|Microsoft Outlook|Microsoft Onenote|Microsoft PowerPoint|Microsoft Publisher|Microsoft Word” -and $_.Update.Title -match “32-Bit”}
              $Officex64 = $approvedupdates | ? {$_.Update.Title -match “Microsoft Office|Microsoft Access|Microsoft Excel|Microsoft Outlook|Microsoft Onenote|Microsoft PowerPoint|Microsoft Publisher|Microsoft Word” -and $_.Update.Title -match “64-Bit”}

              $totalsuperseded = $superseded.count
              $totalitanium = $itanium.count
              $totalSharepoint = $Sharepoint.count
              $totalWin10x86 = $Win10x86.count
              $totalWin10arm64 = $Win10arm64.count
              $totalOfficex86 = $Officex86.count
              $totalOfficex64 = $Officex64.count

              $datum = Get-Date -Format g

              foreach ($update in $superseded)
              {
              $datum + ” [Abgelehnt] ” + ” [Superseded] ” + $update.update.title + “`r`n” | Out-File C:\WSUS_Skript_Bereinigung\Log\1LogAbgelehnteUpdates.txt -Append
              }

              foreach ($update in $superseded)
              {
              Write-Progress -Activity ‘Declining updates’ -Status “$($update.Update.Title)” -PercentComplete (($i/$totalsuperseded) * 100)
              $update.Update.Decline()
              $i++
              }

              $i = 0

              foreach ($update in $itanium)
              {
              $datum + ” [Abgelehnt] ” + ” [itanium] ” + $update.update.title + “`r`n” | Out-File C:\WSUS_Skript_Bereinigung\Log\1LogAbgelehnteUpdates.txt -Append
              }

              foreach ($update in $itanium)
              {
              Write-Progress -Activity ‘Declining updates’ -Status “$($update.Update.Title)” -PercentComplete (($i/$totalitanium) * 100)
              $update.Update.Decline()
              $i++
              }

              $i = 0

              foreach ($update in $Sharepoint)
              {
              $datum + ” [Abgelehnt] ” + ” [Sharepoint] ” + $update.update.title + “`r`n” | Out-File C:\WSUS_Skript_Bereinigung\Log\1LogAbgelehnteUpdates.txt -Append
              }

              foreach ($update in $Sharepoint)
              {
              Write-Progress -Activity ‘Declining updates’ -Status “$($update.Update.Title)” -PercentComplete (($i/$totalSharepoint) * 100)
              $update.Update.Decline()
              $i++
              }
              $i = 0

              foreach ($update in $Win10x86)
              {
              $datum + ” [Abgelehnt] ” + ” [Win10x86] ” + $update.update.title + “`r`n” | Out-File C:\WSUS_Skript_Bereinigung\Log\1LogAbgelehnteUpdates.txt -Append
              }

              foreach ($update in $Win10x86)
              {
              Write-Progress -Activity ‘Declining updates’ -Status “$($update.Update.Title)” -PercentComplete (($i/$totalWin10x86) * 100)
              $update.Update.Decline()
              $i++
              }

              $i = 0

              foreach ($update in $Win10arm64)
              {
              $datum + ” [Abgelehnt] ” + ” [Win10arm64] ” + $update.update.title + “`r`n” | Out-File C:\WSUS_Skript_Bereinigung\Log\1LogAbgelehnteUpdates.txt -Append
              }

              foreach ($update in $Win10arm64)
              {
              Write-Progress -Activity ‘Declining updates’ -Status “$($update.Update.Title)” -PercentComplete (($i/$totalWin10arm64) * 100)
              $update.Update.Decline()
              $i++
              }

              $i = 0

              foreach ($update in $Officex86)
              {
              $datum + ” [Abgelehnt] ” + ” [Officex86] ” + $update.update.title + “`r`n” | Out-File C:\WSUS_Skript_Bereinigung\Log\1LogAbgelehnteUpdates.txt -Append
              }

              foreach ($update in $Officex86)
              {
              Write-Progress -Activity ‘Declining updates’ -Status “$($update.Update.Title)” -PercentComplete (($i/$totalOfficex86) * 100)
              $update.Update.Decline()
              $i++
              }

              $i = 0

              foreach ($update in $Officex64)
              {
              $datum + ” [Abgelehnt] ” + ” [Officex64] ” + $update.update.title + “`r`n” | Out-File C:\WSUS_Skript_Bereinigung\Log\1LogAbgelehnteUpdates.txt -Append
              }

              foreach ($update in $Officex64)
              {
              Write-Progress -Activity ‘Declining updates’ -Status “$($update.Update.Title)” -PercentComplete (($i/$totalOfficex64) * 100)
              $update.Update.Decline()
              $i++
              }

              Write-Host “Total declined superseeded updates: $totalsuperseded” -ForegroundColor Yellow
              Write-Host “Total declined itanium updates: $totalitanium” -ForegroundColor Yellow
              Write-Host “Total declined sharepoint updates: $totalSharepoint” -ForegroundColor Yellow
              Write-Host “Total declined Windows 10 32bit updates: $totalWin10x86” -ForegroundColor Yellow
              Write-Host “Total declined Windows 10 arm64 updates: $totalWin10arm64” -ForegroundColor Yellow
              Write-Host “Total declined Office 32bit updates: $totalOfficex86” -ForegroundColor Yellow
              Write-Host “Total declined Office 64bit updates: $totalOfficex64” -ForegroundColor Yellow

              Invoke-WsusServerCleanup -DeclineSupersededUpdates -DeclineExpiredUpdates -CleanupObsoleteUpdates -CleanupUnneededContentFiles -CompressUpdates

    2. Alex

      Unser WSUS bat vorher 490 GB belegt, ich konnte durch das bereinigen der ersetzten Updates ca 400GB freischaufeln!

      Danke für den Tipp!

      Eine Frage habe ich aber noch zu dem Punkt:
      Weiter geht es an dieser Stelle und wir prüfen ob die Updates die die andere Ersetzen auch genehmigt wurden und zur Installation freigegeben sind.

      Wie prüfe ich das? Ich habe hoffnungsvoll ohne zu prüfen die Anleitung befolgt.

      • JARVIS

        Hi Alex,
        schön das ich helfen konnte und 400GB bzw. ca. 82% ist schon eine ganze Menge. Ich habe den Artikel bzgl. deiner Frage etwas ausführlicher formuliert. Ich hoffe das hilft.

        • Alex

          Danke für die Ergänzung !
          Ich habe noch auf einem weiteren WSUS Server die ersetzten Updates abgelehnt und den Server bereinigt. Ergebnis:
          Vorher ca 560GB belegt, jetz nur noch ca 55 GB ! 🙂

          Ich habe eben noch geprüft: Die Option “Updates automatisch ablehnen, wenn eine neue Revision deren Ablauf bewirkt” war bei uns seltsamerweise schon vorher aktiv…

          Ich habe irgendwo mal gelesen dass man die automatische Genehmigung von Updates noch besser organisieren kann indem man nur Updates genehmigt die auch von Clients angefordert/benötigt werden. Dadurch würden nicht erforderliche Updates auch gar nicht erst vom WSUS runtergeladen werden wodurch man wieder Platz sparen kann.

          Was mir noch beim aufräumen auffiel:
          Wenn ich zB gezielt Updates von Produkten die wir nicht mehr einsetzen ablehnen und somit löschen will, wie kann ich diese Updates eindeutig finden, ohne dabei auch jene Updates mit gelistet zu bekommen die gleichzeitig für andere Betriebssysteme sind?
          Beispiel: Ich will alle XP Updates entfernen und lasse mir diese Anzeigen mit einer exzra Updateansicht für Window XP Produkte. In der Liste stehen dann natürlich auch Updates wie für Windows XP, Server 2008 und Server 2008R2 sind.
          Server 2008R2 haben wir (noch) im Einsatz und möchten deswegen auch Updates erhalten.
          Ich könnte wohl zuerst alle XP ablehnen und dann das gleiche umgekehrt, also wieder genehmigen, mit Updates für 2008R2 machen?

          • JARVIS

            Hi Alex,
            da wird doch echt HDD-Speicher frei :). Ich habe bis auf den Defender keine Updates als automatisch genehmigt. Ich handhabe es so, dass erst eine Testgruppe die neusten Updates erhält und wenn alles gut läuft, nach X-Tagen, alle anderen die Updates erhalten. D.h. Sie werden auch erst bei Genehmigung heruntergeladen und verbrauchen HDD-Speicher. D.h. heißt auch, dass ich Updates wie Office x64 ablehne, da nur x32 im Einsatz ist. Oder Updates für ARM oder älteren Windows Builds. Das hat auch den Vorteil, dass ggf. Updates die an dem Tag als “Fehlerverursachend” bekannt sind, erst Mal nicht genehmigt werden. Nachteil ist der höhere Aufwand…
            Zu deiner Windows XP Frage.
            – zuerst würde ich Windows XP aus “Produkte und Klassifizierung” in den Optionen deaktivieren, dann werden keine neuen runtergeladen
            – dann würde ich die Ansicht auf “alle bis auf abgelehnte” und Status “Alle” einstellen
            – nun die Suchfunktion verwenden und nach “Windows XP” suchen, nun werden dir entspr. alle mit “Windows XP” im Titel angezeigt
            – das Problem ist, dass viele Windows XP Updates auch für Windows Vista, Windows 7, Server 2003 und Server 2008 sind, heißt die brauchst du noch, aber alle anderen könntest du aus der Liste markieren und ablehnen
            – letztendlich noch ein weiteres händisches über die Liste der Updates schauen, ggf. erneut die Suchfuntkion verwenden um mehrere zu finden und aus der Liste abzulehnen
            – ist das getan, kannst du wie im Artikel beschrieben und von dir nun mehrfach durchgeführt, aufräumen 🙂

            • Alex

              Mit Hilfe der Suche habe ich alle Updates für Itanium Systeme abgelehnt, da diese scheinbar wirklich immer exklusiv für Itanium-Systeme sind und somit keine anderen Betriebssysteme betroffen sind von meiner Aktion.
              Das Problem bei der Suche ist, dass nicht alle Updates unbedingt das Produkt im Titel haben. Ich habe das bei alten Office Updates für Version 2003 und/oder 2007 gesehen. Mit der Suchfunktion konnte ich sie nicht finden weil im Titel nur Stand “Office Update KBXYZ123 …” allerdings nicht die Versionsnummer (2003 oder 2007 zB)
              Evtl betrifft das auch nur einige alte Updates aber ich habe erstmal befürchtet und angenommen, dass das wohl generell nicht immer zwingend der vollständige Produktname (also Name + Version) im Titel steht. Ist halt mit Handarbeit verbunden aber geht auch irgendwie 😉

    3. Stefan

      Super Hilfe, konnte mit dieser Anleitung über 7010 Updates ablehnen und damit gerade 440 GB aus der WSUS-Datenbank auf einem 2012er entfernen. Vielen Dank, damit habe ich mir eine Erweiterung der Festplatte sparen können 😉

      • JARVIS

        Gerne – gut das ich helfen konnte. Auf einem meiner neu betreuten Server sah es ähnlich aus. Aufräumen dauert dann zwar am Anfang lange, aber wenn man monatlich dabei bleibt, dann hält sich das in Grenzen und auf Dauer ist es performanter und bereitet weniger Problem, ganz zu schweigen, dass man auch mit dem Reporting vernünftig arbeiten kann. Für dein Angebot habe ich aktuell keine Zeit.

    Schreibe einen Kommentar zu Alex Antworten abbrechen

    Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert