Upgrade eines 3-Knoten WSFC mit Always On Availability Group: Windows Server 2019 + SQL Server 2017 → Windows Server 2025 + SQL Server 2025 (VMware vSphere)

Gilt für: SQL Server 2017 (14.x) · SQL Server 2025 (17.x) · Windows Server 2019 · Windows Server 2025 · Always On Availability Groups · Windows Server Failover Clustering · VMware vSphere

Dieser Artikel beschreibt einen vollständigen, schrittweisen Migrations- und Upgrade-Pfad für einen 3-Knoten Windows Server Failover Cluster (WSFC) mit SQL Server Always On Availability Group von Windows Server 2019 / SQL Server 2017 auf Windows Server 2025 / SQL Server 2025. Die Migration erfolgt auf VMware vSphere mittels eines parallelen Clusters und einer Distributed Availability Group (Distributed AG), bei der alle Produktiv-Namen (Listener, Cluster, Instanz) am Ende erhalten bleiben.

1. Executive Summary & Strategie

Die bestehende Umgebung (3 Knoten, Windows Server 2019, SQL Server 2017 Always On AG) wird in zwei großen Schritten modernisiert: Betriebssystem auf Windows Server 2025 und Datenbank auf SQL Server 2025.

Gewählte Strategie – In-Place Rolling Re-Image (Variante B): Die vorhandenen drei VMware-VM-Hüllen (SQLN01, SQLN02, SQLN03) werden weiterverwendet. Pro Knoten wird die alte System- und Daten-VMDK detached (als Rollback-Anker behalten), eine frische OS-Disk angelegt und Windows Server 2025 + SQL Server 2025 installiert. Die Re-Imaged-Knoten bilden einen neuen WSFC und übernehmen die Daten von der Altumgebung per Distributed Availability Group. Nach Cutover wird auch der letzte Knoten re-imaged; abschließend werden Cluster- und Listener-Name auf die Originalwerte zurückgesetzt.

Warum dieses Vorgehen?

  • VMware-VM-Hüllen bleiben erhalten – vCenter-UUID, MAC-Adressen, Folder-Pfade, Tags, Permissions, Backup-Job-Zuordnungen und Monitoring-IDs sind unverändert. Kein zusätzlicher Kapazitätsbedarf für 3 parallele Test-VMs.
  • Hostnames sind durchgehend konstantsp_dropserver/sp_addserver, Domain-Rejoin oder SPN-Hostname-Bereinigung entfallen. Apps, die per Hostname referenzieren, sind nicht betroffen.
  • Der WSFC Cluster OS Rolling Upgrade unterstützt laut Microsoft nur den Sprung auf die nächste OS-Hauptversion (2022 → 2025). 2019 → 2025 ist nicht unterstützt – ein vollständiges Re-Image des Knotens ist daher zwingend.
  • SQL Server 2017 → 2025 ist als direktes Upgrade dokumentiert; die Distributed AG zwischen den Versionen ist ein produktiver, von Microsoft beschriebener Migrationspfad.

Phasen-Übersicht

PhaseAktionRisiko
0Pre-Migration: Backups, DBCC, Witness vor Migration einrichten, VM-Inventar, VM-Clones als Rollback-Ankerniedrig
1SQLN03 evict → Re-Image → in neuen WSFC (Single-Node) → AG_PROD_NEW + Listener (Temp-IP) + DAG DAG_MIGRATION aufbauenmittel
2SQLN02 evict → Re-Image → Join neuer Cluster + AG — Alter Cluster läuft auf 1 Knoten + Witness (SPOF!)hoch
3Cutover: DAG sync → SYNC-Commit → Failover auf neue AG — Point of no Return (DB-Upgrade auf 2025-Format)hoch
4SQLN01 (Ex-Primary) evict → Re-Image → Join neuer Cluster + AG. Altcluster und alte AG sind aufgelöstniedrig
5Rename neuer Cluster SQLCL01NSQLCL01 und Listener SQLLST01NSQLLST01 mit Original-IP 10.10.10.50. AG-Name bleibt AG_PROD_NEWniedrig
Tipp – Vorteile dieses Vorgehens Sauberes Greenfield-OS · Hostnames bleiben · vCenter-VM-Objekte bleiben (UUID/MAC/Tags) · Alte VMDKs bleiben als Rollback-Anker (DETACH, nicht DELETE) · Minimale Downtime (1 manuelles Failover) · Gelegenheit für moderne VM-Hardware (vHW 21, mehrere PVSCSI, Memory-Reservation).
Wichtig – Namens-Erhaltung als Designziel Hostnames der drei Knoten bleiben durchgehend gleich. Cluster- und Listener-Name laufen während der Migration mit dem Suffix N und werden in Phase 5 auf die Originalnamen zurückgesetzt. Original-Listener-IP 10.10.10.50 wird wiederverwendet. AG-Name bleibt AG_PROD_NEW – ein Rename via DROP+CREATE ist mit Distributed AG / TDE-Datenbanken zu riskant; Apps verbinden sich über den Listener-Namen, nicht über den AG-Namen.
Achtung – Drei explizite Risiko-Gates
  • Safety.AllowProduction = $true für Cutover und Rename.
  • Safety.AcceptPhase2SPOFRisk = $true vor Phase 2 (Alt-Cluster läuft auf 1 Knoten + Witness während SQLN02 re-imaged wird).
  • Safety.AcceptNoRollbackAfterCutover = $true vor Phase 3 (nach DB-Upgrade kein Failback auf SQL 2017 mehr möglich).

2. Voraussetzungen & Kompatibilitätsmatrix

Unterstützte Upgrade-Pfade

KomponenteQuelleZielUnterstützt?
SQL Server2017 (14.x)2025 (17.x)Ja – direkt (MS Learn: Supported version and edition upgrades 2025)
Windows Server20192025Nur Migration oder mehrstufig (2019→2022→2025). Direkter Rolling Upgrade nicht unterstützt.
SQL 2025 auf Win Server 2019Ja (Minimum-OS für SQL 2025 ist Server 2019).
AG mixed-version (Primary 2017 / Secondary 2025)Nur temporär während Rolling Upgrade oder dauerhaft via Distributed AG.

Anforderungen SQL Server 2025

  • x64-CPU, mind. 1,4 GHz (empfohlen ≥ 2,0 GHz), mind. 4 GB RAM (empfohlen deutlich mehr).
  • .NET Framework 4.7.2.
  • Windows Server 2019, 2022 oder 2025 (Datacenter/Standard/Core).
  • Aktuelle SSMS-Version (SSMS 20+ empfohlen für 2025-Features).

Lizenzierung & Edition

  • Edition-Mapping prüfen: SQL 2017 Enterprise → SQL 2025 Enterprise. Web Edition entfällt in SQL 2025.
  • Windows Server 2025 Datacenter empfohlen wegen Live-Migration / unbeschränkter Virtualisierungsrechte.
  • Software Assurance / aktive Lizenzen verifizieren.

3. Namens-Erhaltung (Listener, Cluster, Hostnamen) PFLICHT

Damit Applikationen, Linked Servers, Connection-Strings, SPNs, Kerberos-Delegationen, Backup-Jobs und Monitoring nach der Migration ohne Anpassung weiterarbeiten, müssen die produktiv genutzten Namen am Ende identisch sein. Da bei Variante B die Hostnames durchgehend konstant bleiben, vereinfacht sich die Namens-Matrix erheblich.

3.1 Namens-Matrix

ObjektVor MigrationWährend Phasen 1–4Nach Phase 5 (final)
VMware-VM-Hüllen (UUID/MAC/Folder)SQLN01 · SQLN02 · SQLN03unverändert (Disks getauscht)unverändert
Computer-HostnamenSQLN01 · SQLN02 · SQLN03unverändert (gleiche Namen, frisches OS)unverändert
WSFC-Clustername (CNO)SQLCL01 (Win2019)SQLCL01N (neuer Cluster Win2025)SQLCL01 (Rename in Phase 5)
AG-NameAG_PROD (2017)AG_PROD (alt) + AG_PROD_NEW (neu)AG_PROD_NEW (kein Rename – bewusst)
AG-Listener-Name (VCO)SQLLST01SQLLST01N (Temp-IP)SQLLST01 (Rename in Phase 5)
Listener-IP10.10.10.5010.10.10.51 (temp)10.10.10.50 (wiederverwendet)
SQL-InstanznameDefault MSSQLSERVERidentischidentisch
AD-Computer-Account (Knoten)existiertbehalten + Passwort-Reset (nicht löschen!)identisch
DB-Namen / Service-Accountunverändertunverändertunverändert
Wichtig – AG-Name wird nicht zurückbenannt Eine AG kann in SQL Server nicht über T-SQL umbenannt werden. Ein DROP+CREATE im laufenden Distributed-AG-Setup mit TDE-Datenbanken und Automatic Seeding ist riskant (Seeding-Reset, Endpoint-Rekonfiguration, Backup-Chain-Aushöhlung). Empfehlung: AG_PROD_NEW dauerhaft belassen. Applikationen verbinden sich über den Listener-Namen SQLLST01 – der AG-Name ist nur für DBA-Tools/DMVs sichtbar.

3.2 Strategie pro Objekt

Computer-Hostnamen (SQLN01/02/03)

Bleiben durchgehend gleich. Vor dem Re-Image wird der Knoten evicted, sein AD-Computer-Account wird nicht gelöscht, sondern lediglich das Passwort zurückgesetzt (Set-ADAccountPassword -Reset). Nach erneutem Domain-Join (Re-Image bringt den selben Hostnamen) übernimmt der Server den vorhandenen AD-Account inkl. SPNs, ACLs und Gruppenmitgliedschaften.

Warnung – AD-Computer-Account NICHT löschen Ein Remove-ADComputer würde alle Gruppen­mitgliedschaften, ACLs (insbesondere auf File-Shares/Storage) und automatisch registrierte SPNs zerstören. Bei Re-Use des Hostnamens nur das Computer-Account-Passwort zurücksetzen – der frisch installierte Knoten übernimmt es beim Domain-Join.

Listener-Name & Listener-IP

  • Während Phasen 1–4 läuft auf dem neuen Cluster SQLLST01N mit der temporären IP 10.10.10.51. Der alte Listener SQLLST01 (IP 10.10.10.50) lebt parallel auf dem alten Cluster und bedient die Produktion bis zum Cutover.
  • Nach Cutover und Decommission des Altclusters wird in Phase 5 der Listener auf den Original­namen und die Original-IP umgebogen:
    ALTER AVAILABILITY GROUP [AG_PROD_NEW] REMOVE LISTENER 'SQLLST01N';
    ALTER AVAILABILITY GROUP [AG_PROD_NEW]
    ADD LISTENER N'SQLLST01' (
       WITH IP ((N'10.10.10.50', N'255.255.255.0')),
       PORT = 1433);
    Apps verbinden sich ohne Connection-String-Änderung wieder über SQLLST01.

WSFC-Clustername

Der neue Cluster wird als SQLCL01N gestartet und in Phase 5 nach komplettem Abbau des alten Clusters umbenannt:

(Get-Cluster -Name SQLCL01N).Name = 'SQLCL01'
# Auf allen Knoten:
Restart-Service ClusSvc -Force

AG-Name (bewusst nicht umbenannt)

Die neue AG heißt AG_PROD_NEW und behält diesen Namen. Begründung siehe Alert oben. Apps referenzieren den Listener-Namen, nicht den AG-Namen.

SQL-Instanzname

Default-Instanz MSSQLSERVER bleibt unverändert (Standard-Setup auf jedem Knoten). Bei benannten Instanzen ist der Instanz-Anteil identisch zu setzen.

SPNs & Kerberos

  • Hostname-bezogene SPNs (MSSQLSvc/SQLN01.domain:1433) bleiben gültig, da der Computer-Account erhalten bleibt.
  • Listener-SPNs werden in Phase 5 vom Skript 11-Rename-Resources.ps1 bereinigt und auf SQLLST01 neu gesetzt; danach setspn -X auf Duplicates prüfen.

DNS / TTL

TTL der A-Records SQLLST01 bereits in Phase 0 auf 60 Sekunden senken – verkürzt Cache-Latenz nach Listener-Switch.

Warnung – Naming-Konflikt-Vermeidung in Phase 5 Vor dem Rename muss nachweislich alles aus dem Alt-Cluster bereinigt sein: AD-CNO SQLCL01 entfernt, AD-VCO SQLLST01 entfernt, DNS-A-Record SQLLST01 entfernt, IP 10.10.10.50 nicht mehr in Verwendung. Das Skript 11-Rename-Resources.ps1 bricht ab, falls einer dieser Pre-Checks fehlschlägt.

4. Quorum & Witness – kritische Empfehlung MUSS-PRÜFUNG

Achtung – Risiko aktueller Aufbau Ein 3-Knoten-Cluster ohne Witness überlebt zwar dynamisch den Ausfall eines Knotens (2 von 3 Voten), verliert das Quorum jedoch bei gleichzeitigem Ausfall von zwei Knoten oder bei einem Netzwerk-Split-Brain (z. B. zwei Knoten in einer Partition, ein Knoten allein). Microsoft empfiehlt seit Windows Server 2012 R2 ausdrücklich für jeden Cluster einen Witness – das Voting wird dann dynamisch verwaltet.

Empfohlene Witness-Typen (im VMware-Umfeld)

TypEignungHinweise
Cloud Witness (Azure Storage Account)Sehr gut – v. a. bei mehreren StandortenBenötigt Internet (HTTPS/443) und Azure-Subscription mit Standard GPv2 Storage Account (LRS).
File Share Witness (SMB 2+)Sehr gut, on-prem ohne CloudShare muss außerhalb der Cluster-Knoten liegen, idealerweise auf einem unabhängigen Server/anderem Failover-Cluster.
Disk WitnessEher für FCI mit Shared StorageFür AG ohne Shared Storage in der Regel nicht sinnvoll.

Im neuen Cluster wird im Rahmen dieses Projekts verpflichtend ein Cloud- oder File-Share-Witness eingerichtet (siehe Phase 0, Schritt 0.7 für den alten Cluster, und automatisch durch 06-Build-NewCluster.ps1 für den neuen Cluster).

Wichtig – Witness auch am alten Cluster! Bei Variante B (Re-Use der VM-Hüllen) verbleibt der alte Cluster in Phase 2 zwischenzeitlich auf einem Knoten. Ohne Witness wäre er sofort quorumlos. Skript 01-Preflight-Assessment.ps1 richtet den Witness auf dem alten Cluster ein (Sektion Witness.Old in Config.psd1). Auf dem neuen Cluster erfolgt das Witness-Setup im Skript 06-Build-NewCluster.ps1 (Sektion Witness.New).

5. VMware-Best-Practices vor dem Upgrade

Die folgenden Punkte stammen aus den VMware-Architektur-Whitepapern „Architecting Microsoft SQL Server on VMware vSphere“ und „Microsoft WSFC on VMware vSphere“ sowie den Microsoft-Empfehlungen für virtualisierte WSFC.

VM-Hardware

  • VM-Hardware-Version auf das von der Ziel-ESXi-Version unterstützte Maximum heben (z. B. vHW 21+).
  • PVSCSI-Controller für Daten- und Log-Disks. Mehrere Controller (OS / Data / Log / TempDB) zur IO-Parallelisierung.
  • VMXNET3 als Netzwerkadapter; separate vNICs für Public, Cluster Heartbeat und ggf. Backup.
  • Neueste VMware Tools installieren.

CPU & Memory

  • vCPUs an physikalischer NUMA-Topologie ausrichten; keine breit-überdimensionierten vCPU-Zahlen.
  • Memory Reservation = konfigurierter RAM für SQL-VMs (verhindert Ballooning / Swapping).
  • „Reserve all guest memory (All locked)“ aktivieren.

Cluster & DRS

  • DRS-Anti-Affinity-Regel („VM-VM Separate“) für alle AG-Replicas, damit nie zwei Knoten auf demselben ESXi-Host laufen.
  • VM-Host-Affinity an Cluster-/Standort-Topologie ausrichten (z. B. Site-A vs. Site-B Hosts).
  • HA-Restart-Priorität für SQL-VMs auf „High“, aber kein VMware-HA-Failover anstelle von WSFC-Failover erwarten.

Storage

  • Eager Zeroed Thick oder modernes vVols/vSAN-Profil mit garantierter Performance.
  • Separate Datastores / VMDKs für Data, Log, TempDB, Backup; Storage-Latenzziel < 10 ms (write log idealerweise < 5 ms).
  • Bei AG ist kein Shared-VMDK / RDM erforderlich – jede Replica hat eigene Disks.

Netzwerk

  • MTU 1500 (oder Jumbo Frames konsistent ende-zu-ende, falls AG-Traffic-Volumen es rechtfertigt).
  • Latenz zwischen Replicas möglichst < 5 ms für synchronen Commit.

6. Phase 0 – Vorbereitung & Rollback-Anker T−14 Tage

  • 0.1 Inventarisierung: Edition, Build/CU-Stand, AG-Konfiguration, Listener-Namen/IPs, Datenbanken (Größe, RTO/RPO), Logins, Linked Servers, SQL Agent Jobs, SSIS-Pakete, Cross-Database-Abhängigkeiten, TDE-Zertifikate, CDC/Replikation.
  • 0.2 Data Migration Assistant (DMA) gegen alle DBs ausführen – Breaking Changes / Behavior Changes / Feature Parity prüfen.
  • 0.3 Compatibility Level dokumentieren. SQL 2025 unterstützt ältere Compat-Level; ein Wechsel ist nicht automatisch.
  • 0.4 Vollbackups (FULL + LOG, geprüft) aller AG-DBs + master, msdb, model. Restore-Test in Stage.
  • 0.5 DBCC CHECKDB auf jeder AG-DB – keine Migration mit korrupten DBs.
  • 0.6 Skripte sichern: Logins (SID/Hash), Jobs, Linked Servers, Server-Trigger, Audits, Credentials, Resource Governor, Mail-Profile, Endpoints, Zertifikate (TDE-Cert + Key per BACKUP CERTIFICATE).
  • 0.7 Witness am alten Cluster nachrüsten PFLICHT – der bestehende 3-Knoten-Cluster läuft ohne Witness. Während Phase 2 verbleibt der alte Cluster auf 1 Knoten; ohne Witness wäre er sofort quorumlos:
    Set-ClusterQuorum -Cluster SQLCL01 -CloudWitness -AccountName <StorAcct> -AccessKey <Key>
    # oder
    Set-ClusterQuorum -Cluster SQLCL01 -FileShareWitness '\\fsw01\WitnessSQLCL01$'
    Dies erledigt das Skript 01-Preflight-Assessment.ps1 automatisch (Witness.Old-Sektion in Config.psd1).
  • 0.8 AD-Vorbereitung: für den neuen Cluster (SQLCL01N) ein CNO-Recht auf OU vorbereiten; für den späteren Rename muss der spätere Name SQLCL01 ebenfalls freigegeben werden können. Computer-Accounts SQLN01/02/03 NICHT löschen – sie werden weiterverwendet.
  • 0.9 DNS: A-Records für SQLLST01N (Temp-IP 10.10.10.51) und SQLCL01N registrieren. TTL für SQLLST01 auf 60 s reduzieren (vorbereitend für Listener-Rückkehr in Phase 5).
  • 0.10 Firewall: Ports 1433 (SQL), 5022 (AG-Endpoint), 3343/3389 (Cluster), 445/SMB (Witness), 135+dyn. RPC, 443 (Cloud-Witness).
  • 0.11 VMware-Inventar exportieren: pro VM aktuelle vHW-Version, vCPU, RAM, Datastore-Pfad, Disk-VMDKs, NIC-Portgroups, DRS-Affinity, Tags. Datei dient als Rollback-Referenz für Skript 03-Reimage-VMShell.ps1.
  • 0.12 Cold-Clone aller drei VMs EMPFOHLEN – als zusätzlicher Rollback-Anker (zusätzlich zu den detached VMDKs). Die geklonten VMs bleiben heruntergefahren in einem separaten vCenter-Folder.
  • 0.13 Kommunikation: Wartungsfenster, RACI, Go/No-Go-Kriterien definieren.
  • 0.14 Stage-Probe: Migration in identischer Topologie durchspielen – insbesondere Phase 2 (SPOF) und Phase 3 (DB-Upgrade) zeitlich messen.
Wichtig – Drei Rollback-Anker pro Knoten
  1. Detached OS- und Daten-VMDKs (im Datastore, nicht gelöscht).
  2. Cold-Clone der kompletten VM (separater Folder).
  3. Backups der DBs (FULL+LOG, restore-getestet).
Nach Cutover (Phase 3) ist allerdings nur noch (3) ein realistischer Rollback-Pfad – siehe Abschnitt 13.
Warnung – CDC / Replikation Datenbanken mit Change Data Capture oder Replikation erfordern Sonderschritte vor und nach dem Versions-Upgrade (MS Learn: Upgrading Always On Availability Group Replica Instances).

7. Phase 1 – Re-Image SQLN03 + neuer Cluster & AG T−7 Tage

SQLN03 ist die zweite Sekundär-Replica der alten AG (AG_PROD) – Eviction hat minimalen Einfluss (Primary bleibt SQLN01, Sec1 bleibt SQLN02 + Witness). Ablauf vollständig automatisierbar über die Skripte 02–07 aus C:\Cluster\Automation\.

  • 1.1 Eviction: SQLN03 aus AG-Replica entfernen, aus altem Cluster evictieren, AD-Computer-Account-Passwort zurücksetzen.
    .\02-Evict-Node.ps1 -NodeName SQLN03
    Das Skript prüft, dass SQLN03 nicht Primary ist, entfernt die Replica idempotent, ruft Remove-ClusterNode + Clear-ClusterNode und resettet via Set-ADAccountPassword -Reset.
  • 1.2 VM-Re-Image: VM herunterfahren, Disk-Inventar exportieren, alle vorhandenen VMDKs detachen (nicht löschen), neue OS- und Daten-Disks (PVSCSI je Controller) anlegen, ISO mounten, Boot von CD-ROM.
    .\03-Reimage-VMShell.ps1 -NodeName SQLN03
    vCenter-VM-Objekt (UUID, MAC, Folder, Tags, Permissions) bleibt erhalten – kein Neuanlegen.
  • 1.3 Windows-Setup: Windows Server 2025 unattended installiert (autounattend.xml setzt Hostname SQLN03, Domain-Join, IP). Beim Domain-Join übernimmt der frische Server den bestehenden AD-Computer-Account (Passwort wurde in 1.1 zurückgesetzt).
  • 1.4 OS-Konfiguration:
    .\04-Configure-Windows.ps1 -NodeName SQLN03
    Features (Failover-Clustering, RSAT-Clustering-PowerShell), Firewall-Regeln, Power-Plan „High Performance“, Datendisks formatieren (NTFS 64K, Mountpoints), aktuelles CU.
  • 1.5 SQL 2025 installieren:
    .\05-Install-SQL.ps1 -NodeName SQLN03
    Unattended-Install (gleicher Service-Account, gleiches Daten-Layout, identische Collation), CU einspielen, Enable-DbaAgHadr.
  • 1.6 Neuen WSFC erstellen (Single-Node):
    .\06-Build-NewCluster.ps1 -NodeName SQLN03 -CreateNewCluster
    Erstellt Cluster SQLCL01N auf 10.10.10.49, setzt Witness (Cloud oder File-Share aus Witness.New).
  • 1.7 AG + Listener + DAG aufbauen:
    .\07-Create-AG-DAG.ps1 -NodeName SQLN03 -CreateNewAG
    Legt AG_PROD_NEW als Single-Replica an, Listener SQLLST01N auf 10.10.10.51, danach CREATE AVAILABILITY GROUP DAG_MIGRATION WITH (DISTRIBUTED) + JOIN von der alten Seite – Automatic Seeding startet.
Tipp – Idempotenz Alle Skripte prüfen den aktuellen Zustand und überspringen bereits erledigte Schritte. Bei Fehlern kann gezielt re-runned werden.

8. Phase 2 – Re-Image SQLN02 (SPOF-Phase) T−3 Tage

Achtung – Single Point of Failure Sobald SQLN02 aus dem alten Cluster evicted wird, läuft der alte Cluster auf 1 Knoten + Witness. Ein Ausfall von SQLN01 in diesem Fenster führt zum Komplett-Stillstand bis zur Notfall-Failover auf den neuen Cluster. Phase 2 ist daher die kritischste Phase der Migration. Skripte verlangen explizit Safety.AcceptPhase2SPOFRisk = $true.
  • 2.1 Frische Backup-Verifikation direkt vor Eviction:
    Backup-DbaDatabase -SqlInstance SQLLST01 -Database * -Type Full -CompressBackup
    Test-DbaLastBackup  -SqlInstance SQLLST01 -Database *
  • 2.2 SPOF-Gate setzen: in Config.psd1 Safety.AcceptPhase2SPOFRisk = $true.
  • 2.3 Re-Image-Sequenz für SQLN02:
    .\02-Evict-Node.ps1        -NodeName SQLN02
    .\03-Reimage-VMShell.ps1   -NodeName SQLN02
    .\04-Configure-Windows.ps1 -NodeName SQLN02
    .\05-Install-SQL.ps1       -NodeName SQLN02
    .\06-Build-NewCluster.ps1  -NodeName SQLN02
    .\07-Create-AG-DAG.ps1     -NodeName SQLN02
  • 2.4 Server-Scope-Objekte migrieren (einmalig nach SQLN03 + SQLN02):
    .\08-Migrate-Logins-Jobs.ps1
    Copy-DbaLogin, Copy-DbaAgentJob, Copy-DbaLinkedServer, Copy-DbaCredential, Copy-DbaDbMail, Copy-DbaServerAudit sowie TDE-Zertifikat-Backup/Restore (mit Vault-Passwort).
  • 2.5 DAG-Sync überwachen bis Queue-Drain:
    .\09-Monitor-Sync.ps1   # poll log_send_queue_size + redo_queue_size
Hinweis – DAG mit ungleicher Replica-Anzahl Der Distributed-AG-Sync funktioniert auch mit asymmetrischer Replica-Anzahl (1 Replica neu vs. 1 Replica alt) – das Hinzufügen weiterer Replicas auf der neuen Seite verläuft transparent.

9. Phase 3 – Cutover & Failover Wartungsfenster

Achtung – Point of no Return Beim Failover auf AG_PROD_NEW durchlaufen die Datenbanken ein internes Upgrade auf das SQL-2025-Format. Ein Failback auf SQL 2017 ist danach nicht mehr möglich – nur noch Restore aus Backup. Skript verlangt Safety.AcceptNoRollbackAfterCutover = $true zusätzlich zu AllowProduction = $true.
  • 3.1 Apps stoppen oder in Read-Only (App-seitig oder via Firewall-Block).
  • 3.2 Sync-Status prüfen – Queues = 0:
    .\09-Monitor-Sync.ps1
    # SELECT * FROM sys.dm_hadr_database_replica_states;
  • 3.3 Cutover ausführen:
    .\10-Cutover.ps1
    Setzt DAG auf SYNCHRONOUS_COMMIT, wartet auf „Synchronized“, führt geplantes Failover auf AG_PROD_NEW aus, wartet auf Abschluss des DB-Upgrades.
  • 3.4 Smoke-Tests: Login, Schreib-/Leseoperation, Linked-Server-Aufrufe, kritische Reports.
  • 3.5 Compatibility Level nach erfolgreichem Test schrittweise anheben (Query Store / Baseline vorher sichern):
    ALTER DATABASE [DBName] SET COMPATIBILITY_LEVEL = 170;
  • 3.6 Statistiken erneuern + Index-Wartungspläne prüfen: EXEC sp_updatestats;
  • 3.7 Apps wieder freischalten – Connection-String unverändert (Listener SQLLST01N antwortet, Apps erreichen die neuen Knoten). Der Rückkehr-Rename des Listeners auf SQLLST01 erfolgt in Phase 5 ohne Connection-String-Änderung.

10. Phase 4 – Re-Image SQLN01 & Decommission Alt T+0 … T+3

SQLN01 ist nach Cutover noch der Ex-Primary des alten Clusters und hält die alte AG AG_PROD alleine. Mit der Eviction von SQLN01 zerfällt der alte Cluster vollständig.

  • 4.1 Hypercare (1–3 Tage): Monitoring, Wait-Stats, Query-Store-Regressions-Analyse. Der alte Cluster läuft technisch noch und dient als zusätzlicher Sanity-Anker.
  • 4.2 Distributed AG sauber abbauen (auf alter und neuer Seite):
    ALTER AVAILABILITY GROUP [DAG_MIGRATION] REMOVE AVAILABILITY GROUP ON 'AG_PROD_NEW';
    DROP AVAILABILITY GROUP  [DAG_MIGRATION];
  • 4.3 Alte AG droppen:
    DROP AVAILABILITY GROUP [AG_PROD];
  • 4.4 Re-Image-Sequenz SQLN01 (Ex-Primary):
    .\02-Evict-Node.ps1        -NodeName SQLN01 -ForcePrimary
    .\03-Reimage-VMShell.ps1   -NodeName SQLN01
    .\04-Configure-Windows.ps1 -NodeName SQLN01
    .\05-Install-SQL.ps1       -NodeName SQLN01
    .\06-Build-NewCluster.ps1  -NodeName SQLN01
    .\07-Create-AG-DAG.ps1     -NodeName SQLN01
    Mit Eviction von SQLN01 ist der alte Cluster aufgelöst. -ForcePrimary erlaubt Eviction obwohl SQLN01 noch (alter) Primary ist.
  • 4.5 Altumgebung manuell bereinigen (Voraussetzung für Phase 5):
    • DNS-A-Record SQLLST01 löschen.
    • AD-CNO SQLCL01 löschen (falls noch vorhanden).
    • AD-VCO SQLLST01 löschen.
    • Alte SPNs entfernen: setspn -D MSSQLSvc/SQLLST01.domain.local:1433 <altSvcAcct> (+ FQDN-Variante).
    • IP 10.10.10.50 in IPAM freigeben.
    • nltest /dsgetdc:domain + AD-Replikation abwarten.
  • 4.6 Acceptance-Tests Zwischenstand:
    Invoke-Pester -Path .\12-Acceptance.Tests.ps1 -Output Detailed
Wichtig – Alte VMDKs & Cold-Clones Die detached VMDKs und die Cold-Clones aus Phase 0 erst nach erfolgreicher Phase 5 + Hypercare archivieren oder löschen – sie sind der einzige Rollback-Pfad ohne Backup-Restore (allerdings nur bis Cutover wirksam).

11. Phase 5 – Rename auf Originalnamen T+3 … T+7

Letzter Schritt: Cluster- und Listener-Name zurück auf die Originalwerte. Kein AG-Rename, kein Hostname-Rename. Komplett automatisiert in 11-Rename-Resources.ps1 mit harten Pre-Checks.

  • 5.1 Voraussetzungen prüfen (das Skript bricht ab, falls nicht erfüllt):
    • Kein DNS-A-Record für SQLLST01 vorhanden.
    • Kein AD-Computer-Account SQLCL01 und kein SQLLST01 vorhanden.
    • IP 10.10.10.50 antwortet nicht (Ping).
    • Alle 3 Knoten im neuen Cluster online.
  • 5.2 Rename durchführen:
    .\11-Rename-Resources.ps1
    Reihenfolge: Listener SQLLST01N entfernen → Listener SQLLST01 mit IP 10.10.10.50 anlegen → Cluster-Rename SQLCL01N → SQLCL01 → Cluster-Service-Restart auf allen Knoten → SPN-Cleanup + Neu-Setzen → Duplicate-SPN-Check.
  • 5.3 DNS verifizieren:
    Resolve-DnsName SQLLST01.domain.local
    Test-NetConnection -ComputerName SQLLST01 -Port 1433
  • 5.4 App-Server DNS-Cache leeren (oder warten bis TTL ablaufen ist):
    Invoke-Command -ComputerName $AppServers -ScriptBlock { ipconfig /flushdns }
  • 5.5 DNS-TTL wieder auf Normalwert (z. B. 3600 s).
  • 5.6 Acceptance-Tests final:
    Invoke-Pester -Path .\12-Acceptance.Tests.ps1 -Output Detailed
  • 5.7 Dokumentation: Architekturdiagramm, Runbook, Failover-Test-Protokoll, Quorum-Konfiguration, finale Namens-Matrix aktualisieren.
  • 5.8 DR-Test innerhalb der ersten 30 Tage: Knotenausfall simulieren, Failover-Zeiten messen, Witness-Verlust simulieren.
Warnung – Reihenfolge zwingend Erst die Altumgebung vollständig abbauen (Phase 4.5), dann Listener- und Cluster-Rename. Andernfalls treten Konflikte bei VCO-/CNO-Erstellung, Cluster-Online-Bring oder Listener-Registrierung auf.

12. Automatisierung PowerShell · dbatools · PowerCLI · Pester

Praktisch jeder manuelle Schritt der Phasen 0–5 lässt sich skripten. Ein vollständiges Skript-Set liegt unter C:\Cluster\Automation\ bei (siehe README.txt). Die Skripte sind idempotent, durch Pester-Tests validierbar und beziehen alle Parameter aus einer einzigen zentralen DateiConfig.psd1. Secrets bleiben in einem Vault und werden nur zur Laufzeit als SecureString in den Speicher geladen.

Wichtig – Drei Safety-Gates
  1. Safety.AllowProduction = $true — aktiviert Cutover- und Rename-Skripte.
  2. Safety.AcceptPhase2SPOFRisk = $true — entsperrt Phase 2 (Alt-Cluster verbleibt 1 Knoten + Witness).
  3. Safety.AcceptNoRollbackAfterCutover = $true — entsperrt Phase 3 (DB-Upgrade auf 2025-Format ist nicht reversibel).
Die Gates müssen jeweils einmal vor der jeweiligen Phase auf $true gesetzt werden und sollten danach wieder zurückgesetzt werden.

11.1 Skript-Inventar (Variante B)

PhaseSkriptAufgabeAufruf
Config.psd1 · 00-LoadConfig.ps1Zentrale Konfiguration, Loader + Helper (Get-CfgSecret, Confirm-Step, Start-PhaseTranscript, Assert-ProductionAllowed, Assert-Phase2SPOFAccepted, Assert-CutoverPointOfNoReturn)dot-source pro Skript
001-Preflight-Assessment.ps1DBCC, Backups, Inventory, + Witness am alten Cluster nachrüsten, + VMware-Inventar exportieren, + Cold-Clone-Hinweis.\01-Preflight-Assessment.ps1
1, 2, 402-Evict-Node.ps1AG-Replica entfernen, Cluster-Node evictieren, AD-Account-Passwort RESETTEN (nicht löschen)-NodeName SQLN03 · in Phase 4 zusätzlich -ForcePrimary
1, 2, 403-Reimage-VMShell.ps1VM herunterfahren, Disks-Inventar exportieren, VMDKs detachen (nicht löschen!), neue OS+Daten-Disks, ISO mounten, Boot-Order CD-ROM-first-NodeName SQLN03
1, 2, 404-Configure-Windows.ps1Wartet auf WinRM, Features (Failover-Clustering), Firewall-Regeln, Datendisks formatieren (Mountpoints, 64K)-NodeName SQLN03
1, 2, 405-Install-SQL.ps1SQL Server 2025 unattended (gleicher Service-Account, Layout, Collation) + neuestes CU, Enable-DbaAgHadr-NodeName SQLN03
1, 2, 406-Build-NewCluster.ps1Erster Knoten: Test-Cluster + New-Cluster (Single-Node) + Witness. Folgende Knoten: Add-ClusterNode-NodeName SQLN03 -CreateNewCluster
1, 2, 407-Create-AG-DAG.ps1Erster Knoten: New-DbaAvailabilityGroup (Single-Replica) + Add-DbaAgListener (Temp-IP) + CREATE DISTRIBUTED + JOIN. Folgende Knoten: Add-DbaAgReplica-NodeName SQLN03 -CreateNewAG
2.508-Migrate-Logins-Jobs.ps1einmalig: Logins, Jobs, Linked Servers, Credentials, DbMail, Server-Audits, TDE-Cert.\08-Migrate-Logins-Jobs.ps1
2.5, 309-Monitor-Sync.ps1Polling der Send-/Redo-Queue bis Drain.\09-Monitor-Sync.ps1
310-Cutover.ps1SYNC-Commit, Sync abwarten, Failover, DB-Upgrade-Wait. Beide Risiko-Gates Pflicht..\10-Cutover.ps1
511-Rename-Resources.ps1Pre-Checks (DNS/AD/IP frei), Listener-Rename (Original-IP), Cluster-Rename, SPN-Cleanup, Duplicate-SPN-Check. Kein AG-Rename, kein Hostname-Rename..\11-Rename-Resources.ps1
0–512-Acceptance.Tests.ps1Pester-5 Tests: Cluster-Name, Knoten-State, Witness, AG-Replica-Anzahl, Queues, Listener, Hostname-Konstanz, Duplicate SPNsInvoke-Pester -Path .\12-Acceptance.Tests.ps1 -Output Detailed
Tipp – Pro-Knoten-Sequenz Pro Knoten in Reihenfolge: 02 → 03 → 04 → 05 → 06 → 07. Beim ersten Knoten (SQLN03) zusätzlich die Switches -CreateNewCluster und -CreateNewAG. Reihenfolge der Knoten: SQLN03 → SQLN02 → CUTOVER → SQLN01 → Rename.

11.2 Aufbau der zentralen Config

Config.psd1 ist ein PowerShell-Datafile (Hashtable) – wird per Import-PowerShellDataFile sicher geladen (kein Code-Execute). Der Loader 00-LoadConfig.ps1 prüft Pflichtfelder, weist CHANGE-ME ab, validiert dass Nodes.Order ein Set-Match zu Nodes.All ist und stellt $global:Cfg bereit.

@{
    Project  = 'WSFC-SQL-Upgrade-2025-InPlace'
    Strategy = 'RollingReImage'           # Variante B

    Nodes = @{
        All   = @('SQLN01','SQLN02','SQLN03')
        Order = @('SQLN03','SQLN02','SQLN01')   # Re-Image-Reihenfolge
    }

    Old = @{                                 # bisheriger Cluster (wird aufgeloest)
        ClusterName  = 'SQLCL01'
        ListenerName = 'SQLLST01'
        ListenerIP   = '10.10.10.50'
        ListenerSubnetMask = '255.255.255.0'
        AGName       = 'AG_PROD'
        SqlInstance  = 'SQLLST01'
    }
    New = @{                                 # waehrend Migration und final
        ClusterName    = 'SQLCL01N'          # in Phase 5 -> SQLCL01
        ListenerName   = 'SQLLST01N'         # in Phase 5 -> SQLLST01
        ListenerIPTemp = '10.10.10.51'
        AGName         = 'AG_PROD_NEW'       # bleibt!
        DAGName        = 'DAG_MIGRATION'
        SqlInstance    = 'SQLLST01N'
    }

    ReImage = @{
        IsoPath           = '[ISO] Win2025/Windows2025.iso'
        AutounattendIso   = '[ISO] custom/autounattend-WSFC.iso'
        OsDiskGB          = 120
        DataDiskGB        = 500
        LogDiskGB         = 200
        TempDbDiskGB      = 100
        DetachOldVmdks    = $true            # alte Disks BLEIBEN als Backup
        CloneAsBackup     = $true            # zusaetzlich Cold-Clone
        CloneFolder       = 'Backup-PreMigration'
    }

    Witness = @{
        Old = @{ Type = 'CloudWitness'; StorageAcct = 'sawsfcsqlcl01';  SecretRef = 'SQL2025_CLOUD_WITNESS_KEY' }
        New = @{ Type = 'CloudWitness'; StorageAcct = 'sawsfcsqlcl01n'; SecretRef = 'SQL2025_CLOUD_WITNESS_KEY' }
    }

    Vmware  = @{ VCenterServer = 'vcenter01.domain.local'; ... }
    Sql     = @{ SetupPath = '\\fs\sw\SQL2025\setup.exe'; ServiceAccount = 'DOMAIN\svc_sql'; ... }

    Secrets = @{
        Backend   = 'SecretManagement'
        VaultName = 'WSFC-Migration-Vault'
        Refs = @{
            SaPassword     = 'SQL2025_SA_PWD'
            CertEncryption = 'SQL2025_CERT_ENCRYPTION_PWD'
            ...
        }
    }

    Safety = @{
        RequireConfirmation         = $true
        AllowProduction             = $false   # Cutover + Rename
        AcceptPhase2SPOFRisk        = $false   # Phase 2
        AcceptNoRollbackAfterCutover= $false   # Cutover
    }
}

11.3 Beispiel: Pro-Knoten-Sequenz für SQLN03

# Erster Knoten (SQLN03) - baut auch Cluster + AG + DAG auf
.\02-Evict-Node.ps1        -NodeName SQLN03
.\03-Reimage-VMShell.ps1   -NodeName SQLN03
.\04-Configure-Windows.ps1 -NodeName SQLN03
.\05-Install-SQL.ps1       -NodeName SQLN03
.\06-Build-NewCluster.ps1  -NodeName SQLN03 -CreateNewCluster
.\07-Create-AG-DAG.ps1     -NodeName SQLN03 -CreateNewAG

# Zweiter Knoten (SQLN02) - SPOF-Phase
# Voraus: Safety.AcceptPhase2SPOFRisk = $true
.\02-Evict-Node.ps1        -NodeName SQLN02
.\03-Reimage-VMShell.ps1   -NodeName SQLN02
.\04-Configure-Windows.ps1 -NodeName SQLN02
.\05-Install-SQL.ps1       -NodeName SQLN02
.\06-Build-NewCluster.ps1  -NodeName SQLN02
.\07-Create-AG-DAG.ps1     -NodeName SQLN02

# Server-Scope migrieren + Sync abwarten
.\08-Migrate-Logins-Jobs.ps1
.\09-Monitor-Sync.ps1

# Cutover (Wartungsfenster)
# Voraus: AllowProduction = $true, AcceptNoRollbackAfterCutover = $true
.\10-Cutover.ps1

# Letzter Knoten (SQLN01, Ex-Primary)
.\02-Evict-Node.ps1        -NodeName SQLN01 -ForcePrimary
.\03-Reimage-VMShell.ps1   -NodeName SQLN01
.\04-Configure-Windows.ps1 -NodeName SQLN01
.\05-Install-SQL.ps1       -NodeName SQLN01
.\06-Build-NewCluster.ps1  -NodeName SQLN01
.\07-Create-AG-DAG.ps1     -NodeName SQLN01

# Phase 5: Rename + Tests
.\11-Rename-Resources.ps1
Invoke-Pester -Path .\12-Acceptance.Tests.ps1 -Output Detailed

11.4 Beispiel: VM-Re-Image mit PowerCLI (Auszug aus 03-Reimage-VMShell.ps1)

$vm = Get-VM -Name $NodeName
Stop-VMGuest -VM $vm -Confirm:$false
do { Start-Sleep 5; $vm = Get-VM $NodeName } until ($vm.PowerState -eq 'PoweredOff')

# Disk-Inventar als Rollback-Referenz exportieren
Get-HardDisk -VM $vm | Select Name,CapacityGB,Filename |
    Export-Csv "$($Cfg.Paths.Reports)\Disks-$NodeName.csv" -NoTypeInformation

# VMDKs DETACHEN - bleiben im Datastore
Get-HardDisk -VM $vm | Remove-HardDisk -DeletePermanently:$false -Confirm:$false

# Neue OS-Disk + Daten-Disks
$os = New-HardDisk -VM $vm -CapacityGB $Cfg.ReImage.OsDiskGB -StorageFormat EagerZeroedThick
New-ScsiController -HardDisk $os -Type ParaVirtual -Confirm:$false
foreach ($d in 'Data','Log','TempDb') {
    $hd = New-HardDisk -VM $vm -CapacityGB ($Cfg.ReImage."${d}DiskGB") -StorageFormat EagerZeroedThick
    New-ScsiController -HardDisk $hd -Type ParaVirtual -Confirm:$false
}

# ISO mounten + Boot von CD-ROM
New-CDDrive -VM $vm -IsoPath $Cfg.ReImage.IsoPath -StartConnected:$true
$vm.ExtensionData.SetBootOptions((New-Object VMware.Vim.VirtualMachineBootOptions -Property @{
    BootOrder = @((New-Object VMware.Vim.VirtualMachineBootOptionsBootableCdromDevice))
}))
Start-VM -VM $vm

11.5 Beispiel: AD-Account-Passwort-Reset (Auszug aus 02-Evict-Node.ps1)

# KEIN Remove-ADComputer - sonst gehen Gruppen/ACLs/SPNs verloren
$pwd = [System.Web.Security.Membership]::GeneratePassword(40, 8)
$sec = ConvertTo-SecureString $pwd -AsPlainText -Force
Set-ADAccountPassword -Identity $NodeName -Reset -NewPassword $sec
# Frisches OS uebernimmt beim Domain-Join den vorhandenen Account.

11.6 Beispiel: Cutover mit zwei Asserts (Auszug aus 10-Cutover.ps1)

. (Join-Path $PSScriptRoot '00-LoadConfig.ps1')
Assert-ProductionAllowed
Assert-CutoverPointOfNoReturn

# Sync abwarten
do {
    $q = (Invoke-DbaQuery -SqlInstance $Cfg.New.SqlInstance -Query `
            'SELECT SUM(log_send_queue_size) Q FROM sys.dm_hadr_database_replica_states').Q
    Start-Sleep 5
} while ($q -gt 0)

# SYNC + Failover
Invoke-DbaQuery -SqlInstance $Cfg.Old.SqlInstance -Query @"
ALTER AVAILABILITY GROUP [$($Cfg.New.DAGName)]
MODIFY AVAILABILITY GROUP ON '$($Cfg.New.AGName)' WITH (AVAILABILITY_MODE = SYNCHRONOUS_COMMIT);
"@
Invoke-DbaQuery -SqlInstance $Cfg.New.SqlInstance -Query `
    "ALTER AVAILABILITY GROUP [$($Cfg.New.DAGName)] FAILOVER;"

11.7 Beispiel: Acceptance-Tests (Pester 5)

BeforeAll { . (Join-Path $PSScriptRoot '00-LoadConfig.ps1') }

Describe 'Phase 5 - Final State' {
  It 'Cluster traegt Originalnamen' { (Get-Cluster).Name | Should -Be $Cfg.Old.ClusterName }
  It 'AG-Name ist AG_PROD_NEW (bewusst nicht umbenannt)' {
      Get-DbaAvailabilityGroup -SqlInstance $Cfg.Old.ListenerName `
          -AvailabilityGroup $Cfg.New.AGName | Should -Not -BeNullOrEmpty
  }
  It 'Listener antwortet auf 1433' {
      (Test-NetConnection $Cfg.Old.ListenerName -Port 1433).TcpTestSucceeded | Should -BeTrue
  }
  It 'Keine Duplicate SPNs' { (setspn -X 2>&1 | Out-String) | Should -Not -Match 'found \d+ group' }
  It 'Hostnames unveraendert (Re-Use VM-Huellen)' {
      foreach ($n in $Cfg.Nodes.All) {
          (Invoke-Command -ComputerName $n { $env:COMPUTERNAME }) | Should -Be $n
      }
  }
}

11.8 Orchestrierung end-to-end

  • Azure DevOps Pipeline oder Ansible-Playbook mit einer Stage pro Phase – jede Stage ruft das passende Skript auf und schließt mit dem zugehörigen Pester-Test ab. Failed Test ⇒ Pipeline-Stop.
  • Transcript-Logging (Start-PhaseTranscript) – jeder Lauf hinterlässt einen Audit-Trail unter C:\Cluster\Automation\Reports\ (in Config konfigurierbar).
  • Secrets über SecretManagement-Modul (Azure Key Vault / CyberArk / lokaler SecretStore).
Warnung – Vor Produktiv-Einsatz Config.psd1 vollständig befüllen (alle CHANGE-ME ersetzen), Secrets im Vault hinterlegen, in einer Stage-Umgebung end-to-end testen, dabei Safety.RequireConfirmation = $true und Pester konsequent nutzen. Cutover/Rename erfordern zusätzlich die Risiko-Gates.

13. Häufige Fehlerquellen & Gegenmaßnahmen

Risiko / FehlerUrsacheGegenmaßnahme
Quorum-Verlust während Migration Kein Witness, mehrere Knoten gleichzeitig in Wartung Witness vor dem Upgrade einrichten; nie mehr als einen Knoten gleichzeitig drainen.
AG-Listener nicht erreichbar nach Cutover DNS-Cache, falsche Cluster-IP, fehlender SPN DNS-TTL niedrig setzen; setspn -L prüfen; ipconfig /flushdns auf App-Servern.
Distributed AG „Not Synchronizing“ Endpoint-Port 5022 blockiert, Service-Account ohne CONNECT-Recht, Zertifikatsfehler Firewall + GRANT CONNECT ON ENDPOINT::Hadr_endpoint TO [Login] prüfen; Endpoint-Authentifizierung (Windows vs. Zertifikat) konsistent.
TDE-Datenbank lässt sich auf neuem Replica nicht öffnen TDE-Zertifikat aus master wurde nicht importiert Vor Seeding: Server-Cert & Private Key per BACKUP CERTIFICATE exportieren und auf jedem neuen Knoten installieren.
Performance-Regression nach Compat-Level-Hub Neuer Cardinality Estimator Query Store nutzen; bei Regression: USE HINT('FORCE_LEGACY_CARDINALITY_ESTIMATION') oder Compat-Level zurücksetzen, gezielte Plan-Force.
Cluster Validation Warnungen ignoriert Treiber, Firmware, Netzwerk-Inkonsistenzen Alle Warnungen adressieren – andernfalls verlustig Microsoft-Support.
SQL VMs auf demselben ESXi-Host (Single Point of Failure) Fehlende DRS-Regel VM-VM Anti-Affinity „Separate Virtual Machines“ für alle Replicas.
Memory Ballooning unter Last Keine Memory-Reservation Vollständige Memory-Reservation, ggf. „Lock pages in memory“ für SQL-Service-Account.
Mixed-Version-AG-Fenster zu lang Projektverzögerung Microsoft begrenzt das Fenster informell auf wenige Tage/Wochen – Cutover terminlich fest einplanen.
Setup-Blocker: „Pending reboot“ Offene Windows-Updates Vor Setup: Neustart, shutdown /r /t 0, PendingFileRenameOperations-Registry prüfen.
Duplicate SPN nach Listener-Rename Alte SPNs am Vorgänger-Account nicht entfernt setspn -X ausführen; alte SPNs explizit per setspn -D entfernen, dann erst neue setzen.
Cluster-Rename schlägt fehl („Computer object exists“) Altes CNO noch in AD vorhanden Altes CNO in AD löschen oder umbenennen, AD-Replikation abwarten, dann (Get-Cluster).Name = ....
Listener-Ressource bleibt offline nach Rename VCO in AD existiert noch / Rechte fehlen VCO entfernen oder Cluster-CNO „Create Computer Objects“-Recht auf OU geben.
@@SERVERNAME ungleich Hostname nach Rename sp_dropserver/sp_addserver vergessen Sofort durchführen, SQL-Dienst neu starten; Replikation/Linked-Server prüfen.
App-Server verbinden nach Listener-Switch nicht DNS-Cache, Connection-Pool, langer TTL TTL vorab niedrig; nach Switch ipconfig /flushdns, App-Pool / Service-Restart.
SQL Agent Jobs liefern keine Mails Database Mail Profile / Operatoren nicht migriert In Phase 0.6 explizit sichern, in Phase 1.10 wiederherstellen, Testmail nach Cutover.
VMware Snapshots während Migration I/O-Latenz, Konsistenzrisiken Snapshots nicht als Backup nutzen; vor/nach Setup max. kurze Snapshots, danach konsolidieren.
Domain-Join scheitert nach Re-Image (Variante B) AD-Computer-Account-Passwort nicht resettet, oder Re-Image mit altem Passwort versucht zu joinen 02-Evict-Node.ps1 setzt vor Re-Image Set-ADAccountPassword -Reset. Bei manuellem Vorgehen: vor Domain-Join Passwort resetten und beim Join „Account already exists, use existing“ wählen.
Alt-Cluster verliert Quorum in Phase 2 Witness fehlt — 1 Knoten + kein Witness = 1 Vote, Quorum-Verlust beim kleinsten Glitch Phase-0-Schritt 0.7 verpflichtend (01-Preflight-Assessment.ps1 richtet Witness ein). Vor Phase 2 prüfen: Get-ClusterQuorum -Cluster SQLCL01.
Alte VMDKs nach Re-Image fälschlich gelöscht Remove-HardDisk -DeletePermanently:$true verwendet 03-Reimage-VMShell.ps1 setzt -DeletePermanently:$false. Bei manuellem Eingriff explizit verifizieren — sonst Rollback-Anker zerstört.
Phase 5 Rename bricht ab Alte AD-Objekte, DNS-Eintrag oder IP noch in Verwendung Schritt 4.5 vollständig abarbeiten. 11-Rename-Resources.ps1 bricht mit klarer Fehlermeldung ab und nennt das blockierende Objekt.

14. Rollback-Strategie

Strategie B kennt mehrere Rollback-Ebenen, deren Wirksamkeit von der jeweiligen Phase abhängt:

Rollback-Anker (mit abnehmender Wirksamkeit)

  1. Detached VMDKs pro Knoten — vor jedem Re-Image (Phase 1, 2, 4) werden die alten OS- und Daten-VMDKs detached, nicht gelöscht. Sie bleiben im Datastore und können in vCenter wieder angehängt werden. Einschränkung: nach Re-Image ist die Vertrauensstellung zur Domäne gebrochen (Computer-Account-Passwort wurde resettet) – manuelle Re-Authentifizierung des AD-Accounts mit dem alten Passwort ist nicht möglich. Vollständiger Rollback nur durch Restore eines AD-Account-Backups oder durch Re-Join der alten Disks mit neuer Domain-Mitgliedschaft.
  2. Cold-Clone der kompletten VMs aus Phase 0 — im Datastore-Folder Backup-PreMigration. Power-On möglich (anderer Name nötig, um Konflikt zu vermeiden). Schnellster Weg zu einer funktionsfähigen Alt-Replica vor Cutover.
  3. DB-Backups (FULL + LOG) auf File-Share — einziger Rollback-Pfad nach Cutover.

Wirksamkeit je Phase

  • Phasen 1–2 (vor Cutover): Re-Image rückgängig durch Re-Attach VMDKs + AD-Account-Wiederherstellung oder Cold-Clone-Power-On. Alter Cluster läuft auf den verbliebenen Knoten weiter und kann den Re-Imaged-Knoten als „echten“ Knoten wieder aufnehmen.
  • Phase 3 (Cutover – Point of no Return): Bis zum Failover-Commit (10-Cutover.ps1, vor ALTER ... FAILOVER) kann die Distributed AG abgebrochen werden – Apps blieben auf SQLLST01 (alt) verbunden, Datenverlust = 0. Nach erfolgreichem Failover beginnt das interne DB-Upgrade auf SQL-2025-Format. Failback auf 2017 ist nicht mehr per AG/DAG möglich.
  • Phase 4–5 (nach Cutover): Einziger Rollback – Restore der vor-Cutover-Backups auf eine separat aufgebaute SQL-2017-Instanz und Re-Routing der Apps. Realistische Wiederanlaufzeit: viele Stunden. Daher das Gate AcceptNoRollbackAfterCutover.
Achtung – Point of no Return Mit Abschluss des DB-Upgrades nach Schritt 3.3 (Failover-Commit) ist der Schritt zurück auf SQL Server 2017 nur über Restore möglich, nicht über AG/DAG-Failover. Das Wartungsfenster muss diesen Punkt klar markieren. Frische Backups direkt vor dem Failover sind Pflicht.
Wichtig – Aufbewahrung der Rollback-Anker Detached VMDKs und Cold-Clones nicht direkt nach Phase 5 löschen. Empfehlung: 30 Tage Hypercare-Aufbewahrung, dann archivieren (Datastore-Export) oder freigeben.

15. Go-Live-Checkliste

#PrüfpunktStatus
1Aktuelle Full + Log Backups aller AG-DBs vorhanden & restore-getestet
2DBCC CHECKDB ohne Fehler auf allen AG-DBs
3Witness auf altem Cluster eingerichtet (Cloud oder File Share) — Pflicht für Phase 2
4Witness auf neuem Cluster konfiguriert und „Online“
5Cluster Validation Report für neuen Cluster ohne Errors
6SQL Server 2025 + neuestes CU auf allen re-imaged Knoten
7Logins / Jobs / Linked Servers / TDE-Zertifikate migriert
8Distributed AG „Synchronized“, Queues = 0
9DRS Anti-Affinity-Regel für Replica-VMs aktiv
10Memory-Reservation = konfigurierter RAM auf allen SQL-VMs
11Firewall-Ports 1433 / 5022 / 3343 / 445 / 443 verifiziert
12Applikations-Connection-Strings unverändert lassen (Listener-Name bleibt)
13Backup-/Monitoring-Konfiguration für neuen Cluster bestätigt
14Rollback-Anker pro Knoten dokumentiert (Detached VMDKs + Cold-Clone)
15Wartungsfenster & Kommunikation an Stakeholder versendet
16Namens-Matrix dokumentiert & freigegeben (Hostnames konstant!)
17DNS-TTL für Listener auf 60 s gesenkt (vorbereitend für Phase 5)
18Risiko-Gates: AllowProduction, AcceptPhase2SPOFRisk, AcceptNoRollbackAfterCutover formell genehmigt
19Alte AD-Objekte SQLCL01 & SQLLST01 (VCO/CNO) und DNS-Eintrag SQLLST01 vor Phase 5 bereinigt
20IP 10.10.10.50 in IPAM frei vor Phase 5
21Cluster- (SQLCL01N → SQLCL01) und Listener-Rename (SQLLST01N → SQLLST01) abgeschlossen
22SPNs verifiziert, keine Duplicate SPNs (setspn -X)
23Pester-Acceptance-Tests (12-Acceptance.Tests.ps1) bestanden
24Detached VMDKs & Cold-Clones für 30-Tage-Hypercare aufbewahrt

16. Qualifizierte Quellen

Alle Microsoft-Links verweisen auf die offizielle Dokumentation learn.microsoft.com. Stand der Recherche: 2026-05-18.