A SQL Server Availability Group that works on an on-premises VLAN can fail in subtle ways on EC2: cluster IPs refuse to come online, the listener registers unreachable addresses, failover succeeds but applications remain disconnected, or a brief UDP interruption evicts a healthy node. WSFC was designed around movable virtual addresses and normal Layer 2 behavior; Amazon VPC networking uses ENI ownership and routing semantics instead.
This guide builds a two-node SQL Server 2025 Enterprise Availability Group across two Availability Zones. It assumes Windows Server 2025, an existing Active Directory domain named corp.example.com, AD-integrated DNS, default SQL instances, a file-share witness in a third failure domain, and one listener address in each subnet. SQL Server 2025 Standard is limited to a two-replica, one-database Basic Availability Group; verify edition-specific features in Microsoft's SQL Server 2025 edition matrix.
Architecture: WSFC, AG, and FCI are different layers
Windows Server Failover Clustering is the coordination layer. It tracks membership, quorum, health, dependencies, ownership, and failover decisions; it does not replicate SQL data. An Availability Group is the SQL database-replication layer built on WSFC. Each replica is an independent SQL instance with its own system databases and EBS storage. User-database changes travel through database-mirroring endpoints, normally TCP 5022.
An FCI protects an entire SQL instance and requires storage accessible by every possible owner. Only one node runs that instance. An AG keeps separate database copies and can provide readable secondaries and database-level failover. The AG listener is a virtual network name that lets applications find the current primary.
Use VPC 10.2.0.0/16, subnet A 10.2.8.0/24 in AZ A, and subnet B 10.2.9.0/24 in AZ B. SQLNODE1 uses primary address 10.2.8.10, WSFC address 10.2.8.20, and listener address 10.2.8.30. SQLNODE2 uses 10.2.9.10, 10.2.9.20, and 10.2.9.30. The logical names are SQLCLUSTER.corp.example.com and APPAG-LST.corp.example.com.
Figure 1 — EC2 owns address reachability at the ENI; WSFC owns which reserved cluster or listener address is online. On failover, the role moves and the other subnet's already-reserved listener IP comes online.
Reserves each virtual address on the correct ENI. This decides whether VPC routing can deliver packets at all.
Maintains quorum, resource dependencies, IP online state, DNS registration, and role ownership.
Replicates transaction log over TCP 5022 and serves client sessions through the listener on TCP 1433.
Each node has one ENI with three AWS private IPv4 addresses. Windows keeps the normal NIC on DHCP. WSFC brings the cluster and listener addresses online only as clustered resources. AWS recommends this one-ENI, three-address model and a unique subnet per node in its SQL Server clustering guidance.
What actually changes during cross-subnet failover
| Resource | Before failover | After failover | What does not move |
|---|---|---|---|
| AG primary | SQLNODE1 | SQLNODE2 | Each instance and its EBS volumes stay on its EC2 node. |
| Listener IP | 10.2.8.30 online | 10.2.9.30 online | 10.2.8.30 remains assigned to node 1's ENI; 10.2.9.30 remains assigned to node 2's ENI. |
| Listener DNS | Both A records when RAP=1 | Both A records remain | The client driver chooses the reachable address with MultiSubnetFailover. |
| Cluster quorum | Two nodes plus witness vote | Same voters | The witness is not a SQL replica and contains no database copy. |
Prerequisites
Patch both nodes to the same Windows and SQL Server cumulative-update level. Use comparable, EBS-optimized instances sized to carry the entire production workload alone. Keep data, log, tempdb, and backup paths consistent across nodes. Join both hosts to the same AD domain, run SQL Server under a domain identity such as CORP\svc_sql, and ensure the nodes resolve each other, the domain controllers, cluster name, listener, and witness through AD DNS.
The cluster-creation identity needs local Administrator access on both nodes and permission to create the cluster computer object. The WSFC cluster name object, or CNO, must then create or control the listener virtual computer object, or VCO. Microsoft documents the identities and delegation model in its failover-cluster accounts overview. Use a witness outside both SQL nodes; a two-node cluster without a witness cannot tolerate many single-failure combinations while retaining quorum.
1. Reserve cluster and listener addresses on the ENIs
The secondary addresses are AWS control-plane reservations. They tell the VPC fabric which ENI owns each address. Replace all angle-bracket placeholders before running these AWS CLI v2 commands.
aws ec2 assign-private-ip-addresses `
--network-interface-id <NODE1_ENI> `
--private-ip-addresses 10.2.8.20 10.2.8.30
aws ec2 assign-private-ip-addresses `
--network-interface-id <NODE2_ENI> `
--private-ip-addresses 10.2.9.20 10.2.9.30
aws ec2 describe-network-interfaces `
--network-interface-ids <NODE1_ENI> <NODE2_ENI> `
--query "NetworkInterfaces[].{ENI:NetworkInterfaceId,Subnet:SubnetId,IPs:PrivateIpAddresses[].PrivateIpAddress}" `
--output table
Do not add these four addresses persistently under Windows IPv4 Advanced Settings. AWS warns that guest-level assignment of cluster-managed secondary addresses can produce network interruptions during failover. The control-plane operation is documented under assign-private-ip-addresses.
EC2 does not let a guest claim an arbitrary IP merely by sending gratuitous ARP. An address can look valid inside Windows yet remain unreachable unless EC2 has assigned it to the correct ENI.
Do not use --allow-reassignment to move these addresses during normal AG failover. A multi-subnet listener is not one floating address moved between ENIs; it is two subnet-local IP resources connected by an OR dependency.
2. Keep Windows on DHCP and verify DNS
Get-NetIPConfiguration
Get-NetIPInterface -AddressFamily IPv4 |
Format-Table InterfaceAlias,Dhcp,ConnectionState
Get-DnsClientServerAddress -AddressFamily IPv4
Resolve-DnsName corp.example.com -Type SOA
Resolve-DnsName SQLNODE1.corp.example.com
Resolve-DnsName SQLNODE2.corp.example.com
The production adapter should use DHCP. The VPC DHCP option set must provide DNS servers capable of resolving the AD zone. Do not point domain members only at public DNS or a VPC resolver that lacks conditional forwarding for the domain.
Keeping only the primary address under normal adapter configuration gives WSFC exclusive control over the virtual addresses and avoids duplicate ownership.
The EC2 console can show three ENI addresses while Get-NetIPAddress shows only the primary address during steady state. That is expected. The other addresses appear in the guest only when their cluster IP resources are brought online.
3. Configure security groups and NACLs
| Purpose | Protocol/port | Source |
|---|---|---|
| WSFC heartbeat | UDP 3343 | SQL node SG |
| WSFC join/control | TCP 3343 | SQL node SG |
| RPC mapper/dynamic | TCP 135, 49152-65535 | SQL node SG |
| SMB and validation | TCP 445, ICMPv4 | SQL node SG |
| AG endpoint | TCP 5022 | SQL node SG |
| SQL listener | TCP 1433 | Application SG |
| RDP | TCP 3389 | Management SG only |
$SqlAgSg = aws ec2 create-security-group `
--group-name sql2025-ag-nodes `
--description "SQL Server 2025 AG and WSFC nodes" `
--vpc-id <VPC_ID> --query GroupId --output text
aws ec2 authorize-security-group-ingress --group-id $SqlAgSg --protocol udp --port 3343 --source-group $SqlAgSg
aws ec2 authorize-security-group-ingress --group-id $SqlAgSg --protocol tcp --port 3343 --source-group $SqlAgSg
aws ec2 authorize-security-group-ingress --group-id $SqlAgSg --protocol tcp --port 135 --source-group $SqlAgSg
aws ec2 authorize-security-group-ingress --group-id $SqlAgSg --protocol tcp --port 445 --source-group $SqlAgSg
aws ec2 authorize-security-group-ingress --group-id $SqlAgSg --protocol tcp --port 49152-65535 --source-group $SqlAgSg
aws ec2 authorize-security-group-ingress --group-id $SqlAgSg --protocol icmp --port -1 --source-group $SqlAgSg
aws ec2 authorize-security-group-ingress --group-id $SqlAgSg --protocol tcp --port 5022 --source-group $SqlAgSg
aws ec2 authorize-security-group-ingress --group-id $SqlAgSg --protocol tcp --port 1433 --source-group <APPLICATION_SG_ID>
Domain-controller security groups must accept node-initiated DNS TCP/UDP 53, Kerberos TCP/UDP 88, NTP UDP 123, RPC TCP 135 and 49152-65535, LDAP TCP/UDP 389, SMB TCP 445, Kerberos password-change TCP/UDP 464, and Global Catalog TCP 3268. Add 636/3269 for LDAP/GC over TLS and TCP 9389 where Active Directory Web Services is used. Security groups are stateful; custom NACLs are stateless and must allow both directions and return traffic.
Reference security groups instead of copying instance private IPs into rules. The node-to-node rules then survive instance replacement as long as the replacement ENI receives the same security group. Restrict TCP 1433 to application and DBA management groups; never use 0.0.0.0/0 for SQL or RDP.
A security group is stateful, but a NACL is not. A restrictive NACL must allow the request and return path, including RPC dynamic ports. An apparently correct security group cannot compensate for a dropped NACL return packet.
4. Configure Windows Firewall
$ClusterSubnets = @("10.2.8.0/24","10.2.9.0/24")
New-NetFirewallRule -DisplayName "WSFC UDP 3343" -Direction Inbound -Action Allow -Protocol UDP -LocalPort 3343 -RemoteAddress $ClusterSubnets -Profile Domain
New-NetFirewallRule -DisplayName "WSFC TCP 3343" -Direction Inbound -Action Allow -Protocol TCP -LocalPort 3343 -RemoteAddress $ClusterSubnets -Profile Domain
New-NetFirewallRule -DisplayName "WSFC RPC Mapper" -Direction Inbound -Action Allow -Protocol TCP -LocalPort 135 -RemoteAddress $ClusterSubnets -Profile Domain
New-NetFirewallRule -DisplayName "WSFC RPC Dynamic" -Direction Inbound -Action Allow -Protocol TCP -LocalPort 49152-65535 -RemoteAddress $ClusterSubnets -Profile Domain
New-NetFirewallRule -DisplayName "WSFC SMB" -Direction Inbound -Action Allow -Protocol TCP -LocalPort 445 -RemoteAddress $ClusterSubnets -Profile Domain
New-NetFirewallRule -DisplayName "WSFC ICMPv4" -Direction Inbound -Action Allow -Protocol ICMPv4 -IcmpType 8 -RemoteAddress $ClusterSubnets -Profile Domain
New-NetFirewallRule -DisplayName "SQL AG Endpoint" -Direction Inbound -Action Allow -Protocol TCP -LocalPort 5022 -RemoteAddress $ClusterSubnets -Profile Domain
New-NetFirewallRule -DisplayName "SQL Listener" -Direction Inbound -Action Allow -Protocol TCP -LocalPort 1433 -RemoteAddress <APPLICATION_CIDR> -Profile Domain
Get-NetConnectionProfile
Get-NetFirewallProfile
Microsoft lists UDP 3343 for cluster communication, TCP 3343 for node join, TCP 445, RPC and ICMP in its Windows port requirements.
Opening TCP 1433 proves only client access. It says nothing about heartbeat, cluster membership, endpoint transport, AD authentication, or DNS registration.
Confirm Get-NetConnectionProfile reports DomainAuthenticated. If Windows temporarily classifies the adapter as Public after boot or a DC outage, Domain-profile allow rules may no longer apply even though their definitions are correct.
5. Prepare AD and DNS permissions
The account running New-Cluster needs Create Computer Objects and Read All Properties on the target AD container. The resulting SQLCLUSTER$ CNO needs Create Computer Objects there as well, or the listener VCO must be prestaged and controlled by the CNO.
Import-Module ActiveDirectory
Get-ADComputer -Filter "Name -eq 'SQLCLUSTER' -or Name -eq 'APPAG-LST'" `
-Properties Enabled,DistinguishedName
New-ADComputer -Name "SQLCLUSTER" -SamAccountName "SQLCLUSTER$" `
-Path "OU=Clusters,DC=corp,DC=example,DC=com" -Enabled $false
New-ADComputer -Name "APPAG-LST" -SamAccountName "APPAG-LST$" `
-Path "OU=Clusters,DC=corp,DC=example,DC=com" -Enabled $false
Only run the creation commands when your AD team requires prestaging. In Active Directory Users and Computers with Advanced Features enabled, grant the cluster-creation identity Full Control of the disabled CNO and grant SQLCLUSTER$ Full Control of the disabled listener VCO. Let WSFC enable and manage the objects. Use secure dynamic updates and normally let WSFC create the A records rather than maintaining them manually.
The administrator creates or activates SQLCLUSTER$; then SQLCLUSTER$ creates or controls APPAG-LST$; then the listener network-name resource uses that VCO to register DNS. Granting rights only to the human administrator does not give the CNO permission to create the listener.
6. Install, validate, and create WSFC
Install-WindowsFeature -Name Failover-Clustering,RSAT-Clustering-PowerShell,RSAT-Clustering-Mgmt -IncludeManagementTools
Import-Module FailoverClusters
Test-Cluster -Node SQLNODE1.corp.example.com,SQLNODE2.corp.example.com -Include Inventory,Network,"System Configuration"
New-Cluster -Name SQLCLUSTER `
-Node SQLNODE1.corp.example.com,SQLNODE2.corp.example.com `
-StaticAddress 10.2.8.20,10.2.9.20 -NoStorage
Set-ClusterQuorum -Cluster SQLCLUSTER `
-FileShareWitness "\\<WITNESS_FQDN>\<WITNESS_SHARE>"
Get-ClusterNode
Get-ClusterResource
Get-ClusterResourceDependency -Resource "Cluster Name"
Get-ClusterQuorum
The cluster name should depend on the two IP resources with OR semantics. Each node brings online only the address in its subnet. Microsoft's New-Cluster reference documents multiple static addresses.
AGs use independent EBS volumes, so storage-related validation output needs interpretation. Network, system configuration, domain, driver, RPC, and firewall failures do not. Save the validation report with the change record before creating the cluster.
Get-ClusterResourceDependency -Resource "Cluster Name" should show OR semantics across the two cluster IP resources. An AND dependency would require both subnet addresses online simultaneously and prevents the core name from coming online.
7. Enable Always On and create endpoints
Import-Module SqlServer
Enable-SqlAlwaysOn -ServerInstance $env:COMPUTERNAME -Force
Run that elevated on both nodes. -Force permits the required SQL service restart. Then create the endpoint on both instances:
USE master;
GO
CREATE ENDPOINT [Hadr_endpoint]
STATE = STARTED
AS TCP (LISTENER_PORT = 5022, LISTENER_IP = ALL)
FOR DATABASE_MIRRORING
(
ROLE = ALL,
AUTHENTICATION = WINDOWS NEGOTIATE,
ENCRYPTION = REQUIRED ALGORITHM AES
);
GO
IF SUSER_ID(N'CORP\svc_sql') IS NULL
CREATE LOGIN [CORP\svc_sql] FROM WINDOWS;
GO
GRANT CONNECT ON ENDPOINT::[Hadr_endpoint] TO [CORP\svc_sql];
GO
If the nodes use different service accounts, grant the other replica's account access to the local endpoint. Built-in or non-domain identities require certificate authentication. See Microsoft's T-SQL AG procedure.
The endpoint is server-to-server transport, not the client listener. TCP 5022 carries log blocks and replica control messages. TCP 1433 carries application TDS sessions. They have different authentication, firewall scope, and failure symptoms.
8. Create and seed the Availability Group
On SQLNODE1, create or select a database in Full recovery model and take a full backup. Then create the AG:
ALTER DATABASE [AppDb] SET RECOVERY FULL;
GO
BACKUP DATABASE [AppDb] TO DISK=N'E:\SQLBackup\AppDb_initial.bak'
WITH INIT,CHECKSUM,COMPRESSION,STATS=10;
GO
CREATE AVAILABILITY GROUP [AppAG]
WITH (AUTOMATED_BACKUP_PREFERENCE=PRIMARY, DB_FAILOVER=ON, DTC_SUPPORT=NONE)
FOR DATABASE [AppDb]
REPLICA ON
N'SQLNODE1' WITH
(
ENDPOINT_URL=N'TCP://SQLNODE1.corp.example.com:5022',
AVAILABILITY_MODE=SYNCHRONOUS_COMMIT,
FAILOVER_MODE=AUTOMATIC,
SEEDING_MODE=AUTOMATIC,
SESSION_TIMEOUT=10,
PRIMARY_ROLE (ALLOW_CONNECTIONS=ALL),
SECONDARY_ROLE (ALLOW_CONNECTIONS=READ_ONLY)
),
N'SQLNODE2' WITH
(
ENDPOINT_URL=N'TCP://SQLNODE2.corp.example.com:5022',
AVAILABILITY_MODE=SYNCHRONOUS_COMMIT,
FAILOVER_MODE=AUTOMATIC,
SEEDING_MODE=AUTOMATIC,
SESSION_TIMEOUT=10,
PRIMARY_ROLE (ALLOW_CONNECTIONS=ALL),
SECONDARY_ROLE (ALLOW_CONNECTIONS=READ_ONLY)
);
GO
Use the exact values returned by SERVERPROPERTY('ServerName') as replica names. On SQLNODE2:
ALTER AVAILABILITY GROUP [AppAG] JOIN;
GO
ALTER AVAILABILITY GROUP [AppAG] GRANT CREATE ANY DATABASE;
GO
The secondary-side grant is required for automatic seeding. Microsoft documents this behavior in Automatic seeding for secondary replicas. For very large databases, manual full/log restore with NORECOVERY is often more controllable.
Automatic seeding is a full database transfer over the HADR endpoint. It consumes source read I/O, destination write I/O, transaction-log retention, network bandwidth, and cross-AZ transfer. Schedule large seeds and monitor sys.dm_hadr_automatic_seeding; convenience is not the same as zero impact.
9. Create the multi-subnet listener
Run on the current primary:
ALTER AVAILABILITY GROUP [AppAG]
ADD LISTENER N'APPAG-LST'
(
WITH IP
(
(N'10.2.8.30',N'255.255.255.0'),
(N'10.2.9.30',N'255.255.255.0')
),
PORT=1433
);
GO
Microsoft recommends one static address per participating subnet in a multi-subnet listener. See Configure an availability-group listener. If creation fails, inspect CNO/VCO permissions, DNS dynamic updates, the ENI reservations, and FailoverClustering events before creating manual DNS records.
Creating the listener in Failover Cluster Manager is not equivalent to creating the first listener through SQL Server. Use T-SQL, SSMS, or SQL PowerShell so SQL Server and WSFC agree on the listener metadata. Direct WSFC construction is mainly for documented additional-listener scenarios.
10. Align DNS behavior with client drivers
Listeners created through SQL normally use RegisterAllProvidersIP=1, registering both A records. Modern clients should use MultiSubnetFailover=True so the driver attempts them in parallel:
Server=tcp:APPAG-LST.corp.example.com,1433;
Database=AppDb;
Integrated Security=true;
MultiSubnetFailover=True;
Connect Timeout=30;
For legacy clients that cannot handle multiple addresses, deliberately switch to active-address-only registration and a five-minute TTL:
$Listener = Get-ClusterResource -Name "APPAG-LST"
$Listener | Set-ClusterParameter -Name RegisterAllProvidersIP -Value 0
$Listener | Set-ClusterParameter -Name HostRecordTTL -Value 300
Stop-ClusterResource -Name "APPAG-LST"
Start-ClusterResource -Name "APPAG-LST"
RegisterAllProvidersIP=1 plus a capable driver gives fast failover. Setting it to 0 avoids sequential-address timeouts in old drivers but makes recovery depend on DNS update, replication, and cache expiration. Do not change the server setting without testing the actual application driver and connection pool.
| Client capability | RegisterAllProvidersIP | HostRecordTTL | Expected recovery behavior |
|---|---|---|---|
| Modern supported driver | 1 | Default is normally acceptable | DNS returns both IPs; MultiSubnetFailover=True attempts them in parallel. |
| Legacy sequential driver | 0 | 300 seconds is a common starting point | Only active IP is published, but failover waits on DNS update and caching. |
| Unknown or mixed estate | Do not guess | Measure first | Inventory driver versions and run application-level failover tests before changing cluster properties. |
SSMS success does not prove that a Java service, old ODBC provider, .NET Framework application, or pooled connection layer will recover correctly. Record driver name and version, connection-string behavior, pool retry timing, and observed outage during the failover test.
AWS-specific pitfalls
Gratuitous ARP is not an ownership mechanism. Every WSFC and listener IP must exist as an ENI secondary address. Multi-subnet WSFC uses separate addresses joined by OR dependencies; it does not move one address across subnets.
Do not persist virtual addresses on the guest NIC. EC2 holds three addresses per ENI, but normal Windows NIC configuration remains on DHCP. WSFC adds and removes its addresses as resources. A static guest configuration containing all three addresses can create duplicate ownership and failover-related drops.
A second heartbeat ENI is not physical isolation. A second ENI still shares underlying EC2 infrastructure and bandwidth. AWS therefore notes that it does not reproduce a dedicated physical heartbeat network. Enhanced networking and correct instance sizing matter more than copying an on-premises dual-NIC pattern.
Test-NetConnection -Port 3343 validates TCP join traffic, not ongoing UDP heartbeat health. Event 1135 or an Unreachable node warrants checking the Domain firewall profile, UDP rules, NACLs, packet loss, CPU stalls, and ENI saturation. Microsoft documents a firewall reload failure mode in its WSFC UDP troubleshooting article.
Cross-AZ transaction completion waits for the secondary to harden and acknowledge the log. Measure HADR_SYNC_COMMIT waits, application p95/p99 latency, log-write latency, and cross-AZ transfer cost under realistic load. “Synchronized” proves data state, not acceptable workload latency.
EC2 instance recovery, an Availability Zone failure, a WSFC role move, and a SQL AG failover are different mechanisms. Test them separately. A clean manual ALTER AVAILABILITY GROUP ... FAILOVER does not prove behavior during packet loss, OS failure, host impairment, or a lost Availability Zone.
Verification and failover testing
Verification should prove all four layers: EC2 address ownership, WSFC resource state, SQL synchronization, and client reconnection. A green Failover Cluster Manager view covers only one of them.
Get-ClusterNode
Get-ClusterGroup
Get-ClusterResource | Format-Table Name,State,OwnerGroup,OwnerNode,ResourceType
Get-ClusterQuorum
Resolve-DnsName APPAG-LST.corp.example.com -Type A
With RegisterAllProvidersIP=1, DNS should return both listener addresses while only the address in the primary's subnet is online. Check database synchronization:
SELECT DB_NAME(drs.database_id) AS database_name,
ar.replica_server_name,
drs.synchronization_state_desc,
drs.synchronization_health_desc,
drs.log_send_queue_size,
drs.redo_queue_size
FROM sys.dm_hadr_database_replica_states AS drs
JOIN sys.availability_replicas AS ar ON drs.replica_id=ar.replica_id;
GO
Connect through the listener using a multi-subnet-aware sqlcmd:
sqlcmd -S "tcp:APPAG-LST.corp.example.com,1433" -E -M `
-Q "SELECT @@SERVERNAME AS ConnectedReplica,SYSDATETIMEOFFSET() AS CheckedAt;"
Require SYNCHRONIZED state and synchronous commit before a planned failover. Connect directly to the target secondary and execute:
sqlcmd -S "SQLNODE2.corp.example.com" -E `
-Q "ALTER AVAILABILITY GROUP [AppAG] FAILOVER;"
sqlcmd -S "tcp:APPAG-LST.corp.example.com,1433" -E -M `
-Q "SELECT @@SERVERNAME AS ConnectedReplica,SYSDATETIMEOFFSET() AS CheckedAt;"
Confirm node 2 is primary, 10.2.9.30 is online, 10.2.8.30 is offline, the listener remains online, databases return to synchronized state, and the application reconnects without a new connection string. Fail back and repeat; a one-direction test misses asymmetric rules.
- The listener resolves according to the chosen DNS model.
- The client reconnects using the same DNS name and records an acceptable outage.
- The target becomes primary without forced failover or data loss.
- Both databases return to
SYNCHRONIZEDand queues drain. - No Event 1135, 1205, 1207, endpoint-authentication, or DNS-update errors appear.
- The reverse failover succeeds under the same criteria.
Troubleshooting
| Symptom | Cause and fix |
|---|---|
| IP online in Windows but unreachable | Verify it is assigned to the correct ENI and subnet. Windows configuration alone cannot make it routable. |
New-Cluster cannot create the name | Check CNO permissions, stale AD objects, DNS, TCP 135/445, dynamic RPC, and FailoverClustering events. |
| Listener creation error 19471 | Grant the CNO permission to create/control the VCO and verify both listener addresses are ENI secondary IPs. |
| Half of client connections time out | DNS returns both addresses but the driver tries sequentially. Use MultiSubnetFailover=True or intentionally choose active-only DNS registration. |
| Replica is disconnected | Test TCP 5022, endpoint state, DNS, SQL service identities, and endpoint CONNECT grants. |
| Automatic seeding fails | Check GRANT CREATE ANY DATABASE, disk space, file paths, endpoint traffic, and sys.dm_hadr_automatic_seeding. |
| Event 1135 | Investigate UDP 3343, firewall profile changes, NACLs, packet loss, CPU pauses, and network saturation before relaxing thresholds. |
| Duplicate-address warning | Remove cluster/listener addresses from persistent Windows NIC configuration while leaving them assigned to EC2 ENIs. |
Get-ClusterLog -Cluster SQLCLUSTER -UseLocalTime -TimeSpan 30 -Destination C:\ClusterLogs
Conclusion
A reliable SQL Server 2025 AG on EC2 is not an on-premises cluster copied into two AWS subnets. Its virtual addresses must exist in both control planes: reserved as ENI secondary addresses in EC2 and managed as clustered IP resources in Windows. Once IP ownership, DHCP, AD permissions, firewall paths, DNS registration, and client behavior align, the SQL layer becomes conventional: endpoints, synchronous replicas, seeding, one listener IP per subnet, and repeatable failover testing through the listener.