Kategorien
Allgemein Netzwerk(e) Windows Wissen

IPv6: pfSense hinter FRITZ!Box 6360 (Kabel Deutschland)

Vorab

Ich beschreibe hier lediglich die IPv6-Anbindung. Informationen zu IPv4 sollten selbsterklärend sein. In meinem Szenario verwende ich eine pfSense, die an einer FRITZ!Box 6360 von Kabel Deutschland hängt.

Ich bemühe mich, die Anleitung so allgemein wie möglich zu halten, damit sie möglichst leicht auf eigene Szenarien angewandt werden kann.

Betrachtung der Internetanbindung

Ein erster Blick in den Online-Monitor der FRITZ!Box offenbart uns, dass wir ein /62 Netz erhalten.

In diesem Fall lautet das uns zugewiesene Netz:

  • xxxx:xxxx:dc0:4818::/62

Daraus ergeben sich laut diesem IPv6-Subnetzrechner vier /64 Subnetze. Wenn man ein paar Geräte an die FRITZ!Box hängt und deren IP-Anbindung betrachtet, erkennt man, dass diese Netze zumindest bei der FRITZ!Box 6360 wie folgt verwendet werden:

  • xxxx:xxxx:0dc0:4818::/64 [LAN]
  • xxxx:xxxx:0dc0:4819::/64 [Gastzugang]
  • xxxx:xxxx:0dc0:481a::/64 [Geräte „Heimnetzwerk“]
  • xxxx:xxxx:0dc0:481b::/64 [Geräte „Heimnetzwerk“]

Unser Ziel ist, die letzten beiden /64 Netze (bzw. das daraus resultierende /63 Netz) durch die pfSense an die Clients durchzureichen.

Alle Geräte aus dem Heimnetzwerk sollen eine IPv6-Adresse aus diesem Netz erhalten:

  • xxxx:xxxx:dc0:481a::/63

Erste Vorbereitung(en) auf der FRITZ!Box

Unter Heimnetz -> Heimnetzübersicht findet man den Reiter „Netzwerkeinstellungen“ und hier den Button „IPv6-Adressen“. Dort aktiviert man:

[x] DNS-Server, Präfix (IA_PD) und IPv6-Adresse (IA_NA) zuweisen

Was man damit einschaltet, ergibt sich aus dem entsprechenden Hilfstext:

FRITZ!Box als DNS-Server via DHCPv6 bekannt geben. Teile des vom Internetanbieter zugewiesenen IPv6-Netzes an nachgelagerte Router weitergeben. Geräte im Heimnetzwerk bekommen eine IPv6-Adresse via DHCPv6 zugewiesen.

(Auszug aus der Online-Hilfe der FRITZ!Box 6360)

Das bedeutet:

  • Die FRITZ!Box spielt weiterhin DNS und DHCP für IPv6
  • Die pfSense darf via Router Advertisement ein IPv6-Netz von der FRITZ!Box anfordern und hier Adressen vergeben
  • Geräte, die an die FRITZ!Box angeschlossen werden, bekommen weiterhin eine IPv6-Adresse

Zusätzlich habe ich noch folgende Punkte aktiviert:

[x] Unique Local Adresses (ULA) immer zuweisen
[x] ULA-Präfix manuell festlegen

Das ULA-Präfix sollte man zufällig generieren, bei mir hat der Zufallsgenerator mein Geburtsdatum, gefolgt von abcd ausgeworfen. Was für ein unglaublicher Zufall! 🙂

Die Adresse bei „DNSv6-Server im Heimnetz“ muss man übrigens nicht an die ULA-Adresse der FRITZ!Box anpassen. Zum Einen wird die Option sowieso ignoriert, da die Option nicht aktiviert ist. Zum Anderen wird hier beim Speichern die Adresse von der Fritz!Box eingetragen, falls kein eigener (von der Fritz!Box abweichender) DNS angegeben ist.

Hier ein Bild von meinen Einstellungen:

Die Schnittstellen auf der pfSense

Die WAN-Schnittstelle konfiguriert man als DHCP6 Client.

In den erweiterten Einstellungen legt man die Größe vom Prefix fest, welches die pfSense anfordern soll:

[ ] Only request an IPv6 prefix, do not request an IPv6 address
DHCPv6 Prefix Delegation size: 63
[x] Send an IPv6 prefix hint to indicate the desired prefix size for delegation

Grund, warum ich „Only request an IPv6 prefix, do not request an IPv6 address“ im Gegensatz zu vielen anderen Tutorials nicht(!) anhake ist der, dass ich die IPv6-Adresse der pfSense dann auf der FRITZ!Box „greifbar“ habe (z.B. für Forwardings, Filterregeln, Profile usw. usf.).

Auch hier sagt ein Bild mehr als 1000 Worte:

Bei der LAN-Schnittstelle schauen wir quasi durch die WAN-Schnittstelle auf die Fritz!Box.

Die dazugehörige Schnittstelle wird auf „Track Interface“ gesetzt. In den entsprechenden Optionen ist folgendes konfiguriert:

IPv6 Interface: WAN
IPv6 Prefix ID: 0

Eine andere ID als 0 würde hier keinen Sinn machen. Wir haben ein /63er Netz angefordert, ein /63er Netz erhalten und möchten ein /63er Netz verteilen. Von daher nehmen wir natürlich das Erste und Einzige Netz mit dem Index 0.

Der obligatorische Screenshot:

Auch wenn man bei der Konfiguration mit diversen „Apply Changes“ Anfragen konfrontiert wird: Es empfiehlt sich, die pfSense jetzt einmal neu zu starten.
Nach dem Neustart schaue ich in die Netzwerkeinstellungen unter Status -> Interfaces, ob die pfSense die Adressen übernommen hat. Bei mir sieht das so aus:

Die wesentliche Information findet sich hinter „IPv6 Address“: Hier sollte jetzt eine Adresse aus dem Netz auftauchen, das wir vom Internetanbieter dynamisch zugewiesen bekommen haben. Vergleiche dazu den vorangegangenen Abschnitt „Betrachtung der Netzwerkanbindung“.

Wer jetzt, wie ich, unter chronischer Vergleicheritis leidet, „auf Nummer Sicher“ gehen möchte und ein Gefühl dafür bekommen möchte, welche Adressen wo auftauchen, schaut sich die pfSense in der Heimnetzübersicht der FRITZ!Box an:

„Ja aber bei IPv6 sind doch mehrere Adressen pro Schnittstelle möglich und üblich – müsste dann nicht auch die ULA-Adresse (aus unserem „zufällig zusammengewürfelten“ fd00::/8 Netz) in der Netzwerkübersicht der pfSense angezeigt werden?“

Ja, das müsste sie – wird sie aber nicht 🙂

Die pfSense hat dennoch eine Adresse aus dem ULA-Netz erhalten und kennt diese auch. Wer es nicht glaubt, kann das (auf der pfSense) in einer Shell mit „ipconfig -a“ überprüfen. Es handelt sich schlichtweg um einen Fehler in der Anzeige. Wer es nicht glaubt – ich trete gerne den Beweis an:

Hier sieht man, dass das WAN-Interface drei Adressen vorhält:

  • Die Adresse mit dem Präfix fe80::/64 ist die Link-Local-Adresse
  • Die Adresse aus dem Subnetz xxxx:xxxx:0dc0:481a::/64 stammt von unserem Internetanbieter. Vergleiche dazu den vorangegangenen Abschnitt „Betrachtung der Netzwerkanbindung“
  • Die Adresse aus dem Subnetz fd00::/8 Netz stammt aus unserem ULA-Netz auf der FRITZ!Box

Auf der LAN-Schnittstelle sieht es änlich aus mit dem Unterschied, dass hier keine ULA-Adresse aus dem Netz der FRITZ!Box konfiguriert ist. ULA-Adressen existieren immer nur innerhalb eines Netzwerksegmentes. Die trennende Komponente ist hier die pfSense, so dass die das ULA-Netz zwar ein-, aber nicht ausgehend sieht.

Wir haben jetzt das IPv6-Netz auf der pfSense und fahren mit der Konfiguration der Firewall fort.

Damit die pfSense weiß, wo sie ihren IPv6-Traffic los wird, setzen wir unter System -> Routing -> Gateways das entsprechende Gateway. Das geschieht nicht automatisch (…und ich wäre fast wahnsinnig geworden!).

Auch wenn es noch so verführerisch erscheint: Der DHCPv6-Server wird nicht aktiviert und hier muss auch nichts konfiguriert werden.

Statt dessen setzen wir unter Services -> DHCPv6 Server & RA -> Router Advertisements den „Router Mode“ auf Unmanaged:

Das war’s 🙂

Betrachtung des Ergebnisses

Ein „Windows 10“ PC hängt an der LAN-Schnittstelle der pfSense und bezieht via DHCP seine IPv4- und (hoffentlich auch) seine IPv6-Adresse.

In der Eingabeaufforderung verschaffen wir uns mit ipconfig /a einen ersten Überblick:

Unser Windows-PC hält, IPv6-technisch gesehen, drei Adressen vor:

  • Die Adresse mit dem Präfix fe80::/64 ist die Link-Local-Adresse
  • Die IPv6-Adresse aus dem Subnetz xxxx:xxxx:0dc0:481a::/64 stammt von unserem Internetanbieter
  • Die temporäre IPv6-Adresse aus dem Subnetz xxxx:xxxx:0dc0:481a::/64 stammt von unserem Internetanbieter

Aus Gründen des Datenschutzes wird unter Windows (m.W. seit „Windows 7“) immer eine temporäre Adresse gebildet. Diese wird unter Zuhilfenahme von zufällig generierten Daten generiert bei jedem Neustart neu gebildet. Somit soll sichergestellt werden, dass ein PC nicht anhand seiner IP-Adresse wiedererkannt werden kann.

Weitere Beweise für das Überschreiten der Zielgeraden verschaffen wir uns so:

  • Der ping auf eine „beliebige“ IPv6-Adresse (z.B. 2620:fe::fe, das ist der DNS-Server von Quad 9) verläuft problemlos
  • Der ping auf die ULA-Adresse der FRITZ!Box funktioniert ebenfalls
  • Unsere FRITZ!Box löst den eigenen Hostnamen fritz.box fehlerfrei auf. Als Antwort erhalten wir ihre IP-Adresse aus dem öffentlichen Netz unseres Anbieters und die ULA-Adresse
  • Unsere FRITZ!Box löst die eigene ULA-Adresse revers auf. Als Antwort erhalten wir – wie erhofft – den Hostnamen fritz.box

Ein letzter prüfender Blick geht auf einen der zahlreichen Dienste, mit denen man seine IPv6-Anbindung überprüfen kann. Das Ergebnis:

Und? Was fällt uns auf? Richtig – es wird tatsächlich die temporäre IPv6-Adresse unseres „Windows 10“ PCs verwendet. Seine eigene, ebenfalls öffentliche IPv6-Adresse wird verschleiert – was dem Datenschutz sicher keinen Abbruch tut.

Ich würde mal behaupten, wir sind „fertig“. Fragen, Anregungen und Ergänzungen gerne in den Kommentaren 🙂

Kategorien
Netzwerk(e) Windows Wissen

„Quickie“ – reverse DNS-Einträge mit separatem DHCP-Server

Denkbar sind Szenarien, bei denen DNS- und DHCP-Server nicht auf ein- und demselben System laufen. Ich hatte in einem Testnetz den Fall, in dem DNS über ActiveDirectory lief und IP-Adressen vom Router verteilt wurden.

Das geht tatsächlich und stellt im Großen und Ganzen auch kein Problem dar. Auffällig ist nur, dass für die Clients in dem Fall keine reversen Einträge erstellt werden.

Microsoft schreibt dazu in KB816592:

Standardmäßig ist Adressen dieser Verbindung in DNS registrieren aktiviert und DNS-Suffix dieser Verbindung in DNS-Registrierung verwenden deaktiviert. Bei dieser Standardkonfiguration fordert der Client an, dass die Registrierung des A-Ressourceneintrags durch den Client und die Registrierung des PTR-Ressourceneintrags durch den Server erfolgt.

https://support.microsoft.com/de-de/help/816592/how-to-configure-dns-dynamic-updates-in-windows-server-2003

Auf Deutsch: Der Client trägt sich im DNS ein, der reverse Eintrag erfolgt durch den DHCP-Server.

Dieses Verhalten lässt sich über eine Gruppenrichtlinie ändern. Dort (Default Domain Policy -> Computerkonfiguration -> Richtlinien -> Netzwerk -> DNS-Client) den kann man den Eintrag PTR-Einträge registrieren setzen.

Im Anschluss werden die reversen Auflösungen ebenfalls durch den Client eingetragen.

Kategorien
Netzwerk(e) Windows Wissen

„Quickie“: Unerwünschte Aktivierung von Mozillas „DNS over HTTPS“ in pfSense verhindern

Mozilla macht jetzt wohl wirklich Ernst mit „DNS over HTTPS“ (kurz: DoH). Damit werden sämtliche DNS-Anfragen zu entsprechenden Anbietern geleitet. Der im Betriebssystem konfigurierte DNS-Server wird dabei von Firefox ignoriert.

Ob das jetzt Sinn macht oder nicht, ich persönlich habe mindestens(!) folgende Bedenken:

  • Abhängigkeit von einem externen DNS-Anbieter
  • Der externe Anbieter „fährt“ unter Umständen eine Firmenpolitik, die nicht im Einklang mit den persönlichen Bedürfnissen steht (Möglichkeiten zur Zensur usw.)
  • Datenschutzaspekte – der externe Anbieter erhält Einsicht in alle(!) Internetaufrufe aus dem lokalen Netz
  • keine Erreichbarkeit lokaler Adressen (z.B. http://fritz.box/)
  • Aushebeln von lokalen, DNS-basierten Werbeblockern, Sicherheitsfiltern

Glücklicherweise hat man einen Weg geschaffen, die Aktivierung netzwerkweit zu verhindern. Dies erfolgt über die so genannte „Canary Domain“ (use-application-dns.net). Das Verfahren ist hier beschrieben: https://support.mozilla.org/en-US/kb/canary-domain-use-application-dnsnet

Zusammenfassung: Irgendwann entsteht im Firefox-Browser die Begehrlichkeit, DoH zu aktivieren. In dem Fall wird ein Lookup auf die „Canary Domain“ gestellt und ausgewertet. Folgende Ergebnisse können dabei herauskommen:

  • Die Abfrage liefert keinen NOERROR (NXDOMAIN, SERVFAIL) -> DoH bleibt deaktiviert
  • Die Abfrage liefert einen NOERROR, A- und AAAA-Record sind leer -> DoH bleibt deaktiviert
  • Die Abfrage liefert einen NOERROR und es wird ein A- oder AAAA-Record geliefert -> DoH wird aktiviert

Abhängig vom Ergebnis wird DoH im Anschluss aktiviert. Wichtig ist also, dass die Domain bereits blockiert ist, während dieser einmalige Test läuft. Wird die Domain erst später blockiert, hat das auf ein bereits aktiviertes DoH keine Auswirkung(en).

Wer den DNS-Resolver auf der pfSense nutzt, ist relativ schnell fertig. In der Konsole (SSH-Zugang) legt man eine entsprechende Konfigurationsdatei an:

echo 'local-data: "use-application-dns.net IN CNAME ."' > /var/unbound/ff_canary.conf

Auf der pfSense bindet man diese Konfiguration unter Services -> DNS Resolver -> General Settings ein. Dort gibt es unten ein Feld „Custom Settings“, wo man folgende Zeile ergänzt:

server:include: /var/unbound/ff_canary.conf

Danach startet man den DNS-Resolver einmal über das WebUI neu.

Ob es geklappt hat, kann man z.B. mit einem Windows-Client in der Eingabeaufforderung oder der Powershell mit nslookup überprüfen. Bei mir sieht das in etwa so aus:

C:\Users\altmetaller> nslookup use-application-dns.net
Server: sensenmann.lan.altmetaller
Address: 192.168.11.1

Name: use-application-dns.net

PS C:\Users\altmetaller>

Man sehe und staune – die Domain ist dem DNS bekannt, es wird jedoch kein A- oder AAAA-Record ausgeliefert.

Kategorien
Windows Wissen

WSUS auf einem SBS2011 reparieren (Timeout)

Grundsätzliches bei SBS-Themen

Der SBS2011 ist „nicht ohne“ und verkommt bei schlechter Behandlung zur wiederlichsten Zicke, die Sie in ihrem Leben getroffen haben.

Wenn Sie sich „nur“ mit Computern oder Windows auskennen: Lassen Sie um Himmels Willen die Finger von allem, was außerhalb der „Small Business Standard Konsole“ abläuft. Bitte denken Sie ernsthaft darüber nach, diese Aufgabe (und die Verwantwortung!) zu delegieren.

Wenn Sie sich mit Windows Server 2008r2 und dem Microsoft SQL Server auskennen: Bitte lesen Sie diese Anleitung vollständig und handeln Sie erst dann, wenn Sie der Meinung sind, alles verstanden zu haben.

Die Bereinigung nimmt durchaus mal ein paar Tage Zeit in Anspruch. Das ist aber nicht weiter dramatisch, da das letztendlich im Hintergrund abläuft und die Ressourcenbelastung „eher moderat“ ist.

Meiner Erfahrung nach kann man das durchaus im Geschäftsbetrieb durchführen. Zumindest haben meine Kunden noch nie etwas davon mitbekommen 🙂

Problembeschreibung

Der WSUS-Server vom SBS2011 bzw. 2008r2 „hakt“. Entweder, er liefert gar keine Updates mehr aus oder er holt keine neuen Updates. Der Assistent zur Serverbereinigung läuft auf einen Timeout hinaus.

Was machen wir?

Wir werden den WSUS-Server bzw. dessen Datenbank entschlacken. Gerade bei älteren Systemen wächst diese durchaus auf mehrere Gigabyte an und bremst die Mühle bis zum Stillstand herunter. Erschwerend hinzu kommt beim SBS2011, dass das zugrundeliegende Betriebssystem maximal 32GB RAM verwalten kann. Irgendwann ist also definitiv „Ende Gelände“.

Natürlich werden wir eine Menge löschen, was aber nicht dramatisch ist.

Bei der Aufräumaktion geht es primär darum, historische Protokolle, Statistiken, zurückgezogene oder aus anderen Gründen obsolete Updates zu löschen.

Ab in die Datenbank

Im Startmenü unter „Microsoft SQL Server 2008 R2“ das „SQL Management Studio“ starten und mit dem Datenbankmodul

\.\pipe\MSSQL$MICROSOFT##SSEE\sql\query

verbinden (Windows-Authentifizierung). Hier ein Screenshot vom Anmeldefenster:

Schritt 1: Löschen der Synchronisationshistorie

Wir klicken auf „Neue Abfrage“. Zum Löschen geben wir diesen Text in das Abfragefenster ein:

USE SUSDB
GO
DELETE FROM tbEventInstance WHERE EventNamespaceID = '2' AND EVENTID IN ('381', '382', '384', '386', '387', '389')

Auch wenn es verleitet: Der „! Ausführen“ Button ist der mit dem roten Ausrufezeichen und nicht der Button mit dem grünen Pfeil.

Wir klicken also auf „! Ausführen“ und erhalten wenige Sekunden später das Ergebnis der Löschaktion.

Schritt 2: Löschen aller obsoleten Updates

Bitte das folgende Script im Abfragefenster eingeben:

DECLARE @var1 INT
DECLARE @msg nvarchar(100)
CREATE TABLE #results (Col1 INT) INSERT INTO #results(Col1)
EXEC spGetObsoleteUpdatesToCleanup
DECLARE WC Cursor FOR SELECT Col1 FROM #results
OPEN WC
FETCH NEXT FROM WC INTO @var1 WHILE (@@FETCH_STATUS > -1)
BEGIN SET @msg = 'Deleting ' + CONVERT(varchar(10), @var1) RAISERROR(@msg,0,1) WITH NOWAIT
EXEC spDeleteUpdate @localUpdateID=@var1
FETCH NEXT FROM WC INTO @var1
END
CLOSE WC
DEALLOCATE WC
DROP TABLE #results

Auch hier wieder aus „Ausführen“ klicken. Achtung – dieses Script kann tatsächlich Tage(!) laufen. Aber, keine Angst: Die Serverbelastung durch das Script ist moderat, die Performancebelastung ist nicht bemerkbar.

Dass es noch läuft, sehen wir an der Statusleiste:

Wenn das Script durchgelaufen ist, sieht das Ganze so aus:

Es ist sehr warscheinlich, dass nicht alle Updates gelöscht wurden (z.B. dann, wenn nach der Ausführung bis zur Betrachtung des Ergebnisses eine erneute Synchronisation stattgefunden hat).

Ob wir fertig sind, überprüfen wir mit dieser Datenbankabfrage:

USE SUSDB
GO
exec spGetObsoleteUpdatesToCleanup

Wenn unten im Ergebnisfenster eine Liste mit Nummern angezeigt wird, führt man das Script zum Löschen der obsoleten Updates erneut aus.

Das Ganze machen wir so lange, bis „der Dreizeiler“ keine Updates mehr anzeigt.

Schritt 3: Reindizierung der Datenbank

Der Index der Datenbank (also das „Was steht wo?“) wird anhand des aktuellen Datenbankinhaltes komplett neu aufgebaut.
Hier das passende Script für das Abfragefenster vom SQL-Studio:

/
This sample T-SQL script performs basic maintenance tasks on SUSDB
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
Updates potentially out-of-date table statistics.
/
USE SUSDB;
GO
SET NOCOUNT 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

Für die Laufzeit würde ich maximal 2 Stunden einkalkulieren. Auch hier ist die Systemauslastung eher moderat.

Schritt 4: Verzeichnis aufräumen

Die ganzen Updates „liegen ja irgendwo auf der Platte“ und das Verzeichnis mit dem Inhalt kann tatsächlich enorme Ausmaße beanspruchen.

Um das Verzeichnis aufzuräumen, bediene ich mich eines Powershell-Scriptes.

Performs a cleanup of WSUS.
Outputs the results to a text file.
Adapted and tested by BigTeddy
3 July 2012
$outFilePath = '.\wsusClean.txt'
[reflection.assembly]::LoadWithPartialName("Microsoft.UpdateServices.Administration") | out-null
$wsus = [Microsoft.UpdateServices.Administration.AdminProxy]::GetUpdateServer();
$cleanupScope = new-object Microsoft.UpdateServices.Administration.CleanupScope;
$cleanupScope.DeclineSupersededUpdates = $true
$cleanupScope.DeclineExpiredUpdates = $true
$cleanupScope.CleanupObsoleteUpdates = $true
$cleanupScope.CompressUpdates = $true
$cleanupScope.CleanupObsoleteComputers = $true
$cleanupScope.CleanupUnneededContentFiles = $true
$cleanupManager = $wsus.GetCleanupManager();
$cleanupManager.PerformCleanup($cleanupScope) | Out-File -FilePath $outFilePath

Zur Ausführung des Scriptes speichert man dieses unter einem einprägsamen Namen (z.B. „WSUS-Cleanup.ps1“) und startet eine Powershell als Administrator.

Danach wechselt man in das Verzeichnis mit dem Script, startet es und findet im gleichen Ordner nach erfolgter Ausführung die Datei wsusClean.txt mit dem Ergebnis:

SupersededUpdatesDeclined : 7
ExpiredUpdatesDeclined : 0
ObsoleteUpdatesDeleted : 0
UpdatesCompressed : 19717
ObsoleteComputersDeleted : 0
DiskSpaceFreed : 46590272

Zugegeben: Der Speicherplatzgewinn ist in diesem Beispiel nicht sooooo dramatisch. Aber vielleicht hängt das ja auch damit zusammen, dass ich diese Anleitung kurz nach einer erfolgreichen Bereinigung geschrieben habe 🙂