Migrating SQL Server 2017 to 2025 Using a Distributed Availability Group — A Real-World Proof of Concept
Last week, we reached a significant milestone. In a development environment, we have 16 databases synchronizing cleanly across a DAG that bridges the old stack to the new. The two clusters live in separate domains, run in AWS, and use AWS Storage Gateway for backup-based initialization. There were real obstacles to get here. This post documents the approach and the hard-won lessons.
Executive Summary
When you are staring down a multi-terabyte SQL Server footprint running on aging infrastructure — SQL Server 2017 on Windows Server 2016 — and there is no supported in-place upgrade path that gets you all the way to SQL Server 2025 on Windows Server 2025, you need a different strategy. We found one: the Distributed Availability Group (DAG).
Last week, we reached a significant milestone. In a development environment, we have 16 databases synchronizing cleanly across a DAG that bridges the old stack to the new. The two clusters live in separate domains, run in AWS, and use AWS Storage Gateway for backup-based initialization. There were real obstacles to get here. This post documents the approach and the hard-won lessons.
The Problem
Our client's environment looked like this:
Source: SQL Server 2017, Windows Server 2016, on-premises-style deployment in AWS
Target: SQL Server 2025, Windows Server 2025, new AWS infrastructure
Data footprint: Several terabytes across 16 databases
Constraint: No in-place upgrade path exists that spans this version gap cleanly, and the Windows Server upgrade compounds the problem
The goal was to migrate with minimal downtime and maximum control — not a backup/restore weekend cutover, not a lift-and-shift that leaves you holding your breath. We wanted a live, synchronized target we could validate thoroughly before flipping the switch.
A Distributed Availability Group fit that requirement well. A DAG connects two separate Availability Groups — each in its own WSFC cluster — and replicates data between them. You get asynchronous replication across topologies that traditional AGs cannot span.
The Architecture
┌─────────────────────────────────┐ ┌─────────────────────────────────┐
│ OLD DOMAIN (retiring) │ │ NEW DOMAIN (target) │
│ Windows Server 2016 │ │ Windows Server 2025 │
│ SQL Server 2017 │ DAG │ SQL Server 2025 │
│ │ ──────► │ │
│ WSFC Cluster (AG1) │ │ WSFC Cluster (AG2) │
│ Primary Replica │ │ Secondary Replica │
│ 16 Databases │ │ 16 Databases (synchronizing) │
└─────────────────────────────────┘ └─────────────────────────────────┘
│ │
└───────────────── AWS VPC ────────────────────┘
Storage Gateway
(Backup initialization)
The old cluster is the global primary. The new cluster is the forwarder. When we are ready to cut over, we fail over the DAG, promote the new cluster to primary, and the old infrastructure becomes expendable.
What Made This Hard
1. Cross-Domain Without a Trust
The two clusters live in different Active Directory domains. We deliberately chose not to establish a domain trust between them. The reason matters: we are retiring the old domain entirely, and Windows has a long memory. Domain trust remnants, stale SPN registrations, and lingering DNS entries from a retired domain have a way of causing mysterious failures long after you think the old environment is gone.
The solution was a one-way approach. We were comfortable with the old domain knowing about the new domain, but not the reverse. We changed the SQL Server service account on the old cluster to use a domain account from the new domain. This gave the old SQL Server instance an identity that the new environment would recognize and trust, without creating any dependency in the new domain on the old one.
This is a subtle but important architectural decision. When the old domain is eventually decommissioned, the new environment has no orphaned references to clean up.
2. DAG Endpoint Connectivity on Port 5022 in AWS
SQL Server Availability Group endpoints communicate on port 5022 by default. In a straightforward on-premises setup, this is a firewall rule. In AWS, it is a layered problem: Security Groups, Network ACLs, route tables, and potentially VPC peering or Transit Gateway configurations all have to agree.
We ran into connectivity issues getting the DAG endpoints to communicate reliably between the old and new clusters. The AWS network configuration required specific attention to Security Group inbound/outbound rules on both sides, and verifying that the endpoint was actually reachable — not just that the port appeared open — required careful testing with tools like Test-NetConnection and direct endpoint state inspection via T-SQL.
If you are building this in AWS, validate endpoint reachability explicitly before attempting DAG configuration. A DAG that fails to connect at the endpoint level can produce misleading error messages that look like configuration problems when the root cause is network.
3. Storage Gateway Permissions for Backup Initialization
A DAG secondary must be initialized from backup. Our backup target is AWS Storage Gateway, presented as a file share accessible to both the old and new infrastructure. The old servers had no issues — they had been writing to this location for some time. The new servers were a different story.
The new SQL Server 2025 instances needed access to the Storage Gateway share to restore the initialization backups, and the service account permissions were not in place. This is easy to overlook when you are standing up new infrastructure: the new servers are clean slates, and they do not inherit the access rights that the old servers accumulated over time.
The fix was straightforward once identified — grant the SQL Server service account on the new nodes appropriate access to the Storage Gateway share — but it was one of those issues that stalls you while you work through the symptom before you find the cause.
Where We Are Now
This is a development environment. Production has additional considerations — VPC configurations, backup schedules, AG health policies, endpoint encryption certificates, monitoring, and the cutover runbook itself. There is meaningful work ahead.
But the proof of concept is behind us. Sixteen databases are synchronizing across a distributed availability group that spans two SQL Server versions, two Windows Server versions, two Active Directory domains, and the complexities of AWS networking and shared storage. That is the hard part of "will this work" answered.
The DAG approach gives us something that a traditional migration does not: a live, queryable, continuously-synchronized target environment that we can validate against production workloads before any cutover occurs. When we pull the trigger, it will be with high confidence — not hope.
Key Takeaways
The DAG is an underutilized migration tool. Most documentation treats it as a disaster recovery technology. For version-gap migrations with large data footprints, it deserves serious consideration as a migration mechanism.
Cross-domain without trust is achievable. Using a service account from the target domain on the source cluster is an elegant solution that avoids long-term domain dependency debt.
AWS adds networking complexity that on-premises deployments do not have. Treat port 5022 reachability as something to prove, not assume.
New servers start with no inherited permissions. Audit Storage Gateway (or any shared backup target) access rights for new nodes before you need them.
Validate early and in dev. The friction of finding these issues in a development environment is a fraction of what it would be under production cutover pressure.
What's Next
The next phase moves this from a working proof of concept to a production-ready migration plan. That includes hardening the endpoint configuration, establishing monitoring on the DAG health state, defining the cutover runbook, and validating application connectivity against the new listener.
← All posts