WSUS Server komplett automatisieren

Posted on: 7. Juni 2018
Da der WSUS Server ein zentraler, und wichtiger Dienst für die gesamte Domäne und alle Computer zur Verfügung stellt, werden wir diesen einmal komplett automatisieren. Das Ziel ist, dass möglichst wenig oder kein menschlicher Eingriff mehr nötig ist und trotzdem alle Computer und Server mit den jeweils nötigen Updates versorgt werden.

Bei unserem Konzept haben wir jeweils 2 Pilot-Gruppen je eine für Server und Workstations. Die „Piloten“ erhalten alle Updates jeweils 7 Tage nach Erscheinen. So können wir verhindern, dass ein Update kurzfristig von Microsoft wieder zurückgezogen wurde. Sofern die „Piloten“ kein negatives Feedback zu einem Update oder grundsätzlich zur Stabilität ihrer Computer oder Server geben, werden die Updates ohne zutun nach einer gewissen Zeit freigegeben für alle anderen Computer und Server.
Natürlich werden kritische und sicherheitsrelevante Updates sofort und ohne Verzögerung an alle Computer verteilt, ebenso die Definitions Updates von Windows Defender.
Hier der Code aus dem Youtube-Video. Die Vorgehensweise und Bemerkungen findest du direkt als Kommentare (#Grün) im Code. So wird er dir ebenfalls in der PowerSHELL ISE angezeigt werden.
——————————————–
# Firmenname für LogDatei            
$Firma = "www.germanpowershell.com"            
            
# Zeitspanne für Pilottest festlegen            
$PilotTage = 30 # Ab diesem Alter werden die Updates für alle freigegeben            
            
# Ordner für WSUS Protokoll und Pilot-Update-Liste, inkl. \ am Ende            
$ProtokollOrdner = "D:\WSUSLog\"            
            
# WSUS Server            
$WSUSName = 'DEVIDIA-II'            
            
# Verschlüsselte SSL Verbindung? $true oder $false            
$SSLVerbindung = $False            
            
# TCP Port der WSUS IIS Website            
$PortNummer = 8530            
            
# WSUS Framework laden, falls das Script nicht auf dem WSUS Server lokal ausgeführt wird            
[void][reflection.assembly]::LoadWithPartialName("Microsoft.UpdateServices.Administration")               
            
# Verbindung zum definierten WSUS Server            
# Weitere Infos zur IUpdateServer Class unter            
# -> http://msdn.microsoft.com/en-us/library/microsoft.updateservices.administration.iupdateserver(VS.85).aspx            
$WSUS = [Microsoft.UpdateServices.Administration.AdminProxy]::getUpdateServer($WSUSName, $SSLVerbindung, $PortNummer)               
            
# Globale Variabel für LogDatei             
$LogDatei = ""            
            
# Funktion um Log Informationen in die LogDatei zu schreiben            
function Write-Logfile ($LogText)            
    {            
            
    # Generieren des Dateinamens für die LogDatei pro Monat            
    $global:LogDatei = $ProtokollOrdner + "WSUSUpdateLog-" +(Get-Date -Format "yyyy.MM") +".txt"            
            
    # Testen ob Logdatei bereits vorhanden            
    if (Test-Path $LogDatei)            
                
        {            
            # Hinzufügen des LogTextes zur Logdatei            
            Add-Content $LogDatei $LogText            
        }            
                
    else             
                
        {            
            # Erstellen der LogDatei            
            New-Item $LogDatei -ItemType File -Force | Out-Null            
                        
            Write-Logfile -LogText ($Firma + " Log Datei erfolgreich erstellt " + $LogDatei + " " +(Get-Date).ToString())            
                        
            # Hinzufügen des LogTextes zur Logdatei            
            Add-Content $LogDatei $LogText            
        }            
    }            
            
Write-Logfile -LogText ("Automatische Bereinigung gestartet am" + (Get-Date).ToString())            
            
# Auslesen aller Updates mit Lizenz-Abkommen...            
$Lizenz = $WSUS.GetUpdates() | where {$_.HasLicenseAgreement -eq $true}            
            
# Falls Updates mit Lizenzabkommen vorhanden sind...            
if ($Lizenz.count -gt 0)            
    {            
        foreach ($Update in $Lizenz)            
            {            
                # ... Lizenz-Abkommen akzeptieren            
                $Update.AcceptLicenseAgreement()            
                Write-Logfile -LogText ("Lizenz akzeptiert für Update " + $Update.Title)            
            }            
    }            
            
            
# Auslesen aller unbestätigter Defender Updates            
$Defender = $WSUS.GetUpdates() | where {$_.Title -like "*Definition Update for Windows Defender*" -and $_.IsApproved -eq $False}            
            
# Falls Defender Updates vorhanden sind...            
if ($Defender.count -gt 0)            
    {            
        foreach ($Update in $Defender)            
        {            
            # ... Updates bestätigen für alle Computer            
            $Update.Approve('All Computer')            
            Write-Logfile -LogText ("Defender Update für alle Computer bestaetigt " + $Update.Title)            
        }            
    }            
            
# Auslesen aller Updates im Beta-Status die noch nicht abgelehnt wurden            
$Beta = $WSUS.GetUpdates() | where {$_.isBeta -eq $true -and $_.IsDeclined -eq $False}            
            
# Falls Beta-Updates vorhanden sind...            
if ($Beta.count -gt 0)            
    {            
        foreach ($Update in $Beta)            
        {            
            # ... jedes Beta-Update ablehnen            
            $Update.Decline()            
            Write-Logfile -LogText ("Beta Update abgelehnt " + $Update.Title)            
        }            
    }            
            
# Auslesen aller abgelösten Updates die noch nicht abgelehnt wurden...            
$Abgeloest = $WSUS.GetUpdates() | where {$_.isSuperseded -eq $true -and $_.IsDeclined -eq $False}            
            
if ($Abgeloest.count -gt 0)            
    {            
        foreach ($Update in $Abgeloest)            
            {            
                # ... jedes abgelöste Update ablehnen            
                $Update.Decline()            
                Write-Logfile -LogText ("Abgeloestes Update abgelehnt " + $Update.Title)            
            }            
    }            
            
$WSUSinfra = $WSUS.GetUpdates() | where {$_.isWsusInfrastructureUpdate -eq $true -and $_.IsApproved -eq $False -and $_.IsDeclined -eq $False}            
            
if ($WSUSinfra.count -gt 0)            
    {            
        foreach ($Update in $WSUSinfra)            
            {            
                # ... jedes WSUS Infrstructure Update annehmen für alle Computer            
                $Update.Approve('All Computers')            
                Write-Logfile -LogText ("WSUS Infrastruktur Update bestaetigt " + $Update.Title)            
            }            
    }            
            
# Auslesen aller Wichtigen Updates die nicht bestätigt und nicht abgelehnt wurden...            
$WichtigeUpdates = $WSUS.GetUpdates() |  ?{$_.IsApproved -eq $false -and $_.IsDeclined -eq $false -and $_.UpdateClassificationTitle -in ("Critical Updates","Security Updates")}            
if ($WichtigeUpdates.count -gt 0)            
    {            
        foreach ($Update in $WichtigeUpdates)            
            {            
                # ... jedes wichtige Update annehmen für alle Computer            
                $Update.Approve('All Computers')            
                Write-Logfile -LogText ("Kritische und Sicherheits-Update bestaetigt fuer alle Computer " + $Update.Title)            
            }            
    }            
            
# Auslesen aller abgelaufenen Updates die nicht bestätigt und nicht abgelehnt wurden...            
$Abgelaufen = $WSUS.GetUpdates() |  ?{$_.IsApproved -eq $false -and $_.IsDeclined -eq $false -and $_.PublicationState -eq "Expired"}            
if ($Abgelaufen.count -gt 0)            
    {            
        foreach ($Update in $Abgelaufen)            
            {            
                # ... jedes abgelaufene Update ablehnen            
                $Update.Decline()            
                Write-Logfile -LogText ("Abgelaufenes Update abgelehnt " + $Update.Title)            
            }            
    }            
            
if (Test-Path ($ProtokollOrdner + "\pilotliste-cli.xml"))            
        {            
            # Vorhandene Pilotliste importieren            
            $PilotListe = Import-Clixml ($ProtokollOrdner + "\pilotliste-cli.xml")            
        }             
    else             
        {            
            # Leeres Array für die Pilotliste erstellen            
            $PilotListe = @()            
        }            
            
# Jedes Update das weder bestätigt noch abgelehnt wurde und benötigt wird abfragen            
$Pilot = $WSUS.GetUpdates() |  ?{$_.IsApproved -eq $false -and $_.IsDeclined -eq $false -and $_.ArrivalDate -lt (Get-Date).AddDays(-7) -and $_.State -ne "NotNeeded"}            
if ($Pilot.count -gt 0)            
    {            
        foreach ($Update in $Pilot)            
        {            
            # Ergänzen der Pilotliste mit neu freigegebenem Update            
            $PilotListe += ,@($Update.Id.UpdateId.Guid;Get-Date -Format "dd.MM.yyyy")            
            
            # Alle Pilotgruppen auslesen            
            $Pilotgruppen = $WSUS.GetComputerTargetGroups() | where {$_.Name -like "*pilot*"}            
            
            # Updates für die Pilotgruppen freigeben            
            $Update.Approve($Pilotgruppen)            
            Write-Logfile -LogText ("Update bestaetigt fuer Pilot-Computer " + $Update.Title)            
        }            
    }            
            
# Exportieren und speichern der neuen Pilotliste            
$PilotListe | Export-Clixml -Path ($ProtokollOrdner + "\pilotliste-cli.xml") -Encoding Default -Force            
            
for ($i = 0; $i -lt $PilotListe.Count; $i ++)            
    {            
            
    # Prüfen ob das Update bereits über die Alterslimite an die Pilot-Computer verteilt wurde            
    if ($PilotListe[$i][1] -lt (Get-Date).AddDays(-$PilotTage))            
        {            
            # Abrufen und bestätigen des Updates für alle Computer            
            Get-WsusUpdate -UpdateId $PilotListe[$i][0] | Approve-WsusUpdate -Action Install -TargetGroupName 'All Computers'            
            Write-Logfile -LogText ("Update fuer alle Computer freigegeben " + (Get-WsusUpdate -UpdateId $PilotListe[$i][0]).Update.Title)            
                        
            # Bereinigen des Update-Eintrags auf der Pilotliste            
            $PilotListe[$i].Clear()            
        }            
            
    }            
            
# Neue Pilotliste ohne die bestätigten Updates            
$PilotListe = $PilotListe | ? {$_ -ne $null}            
            
# Speichern der neuen Pilotliste            
$PilotListe | Export-Clixml -Path ($ProtokollOrdner + "\pilotliste-cli.xml") -Encoding Default -Force            
            
Write-Logfile -LogText ("WSUS Cleanup Befehl wird gestartet... "+ (Get-Date).ToString())            
            
# Aufrufen des WSUS Cleanup Befehls mit allen Parametern um möglichst komplett zu bereinigen            
Invoke-WsusServerCleanup -UpdateServer $WSUSName -CleanupObsoleteComputers -CleanupObsoleteUpdates -CleanupUnneededContentFiles -CompressUpdates -DeclineExpiredUpdates -DeclineSupersededUpdates >> $LogDatei            
            
Write-Logfile -LogText ("WSUS Cleanup Befehl beendet "+ (Get-Date).ToString())            
            
Write-Logfile -LogText ("Automatische Bereinigung beendet am" + (Get-Date).ToString())
——————————————–
Solltest du irgendwelche Fragen haben, dann kannst du diese gerne entweder hier im Blog oder direkt unter dem YouTube Video stellen. Ich gebe alles, deine Fragen zeitnahe zu beantworten oder ein entsprechendes Video darüber zu machen.
Gerade die Grundlagen-Videos werde ich nicht mit viel Text ausschmücken. Bei mir ist jeweils jede Code-Zeile direkt im Code Kommentiert (#Grün). Du kannst den Code direkt mit in dein Script kopieren und hast so eine gute Orientierungshilfe, sobald du die Scripts auf deine Bedürfnisse anpasst.

Ich erstelle und veröffentliche sämtliche Videos kostenlos und blende auch keine Werbung ein, dies will ich auch so beibehalten. Da das vermittelte Wissen aber in teuren Kursen erlangt werden kann, ist eine kleine Spende hin und wieder sicher keine schlechte Gegenleistung.