Tested on SQL Server 2022, October 2026: the distributed availability group (create, join, automatic seeding and the health check) worked between two test instances, and the FCI status queries run. The FCI PowerShell commands and failover have since been tested on a Windows Server failover cluster.
Failover Cluster Instances (FCI)
An FCI is one SQL Server instance installed across several Windows cluster nodes, with its databases on shared storage. Only one node runs the instance at a time; if it fails, the cluster starts the instance on another node. It protects against server failure, not storage failure, and everything in the instance (logins, jobs, msdb) moves with it.
-- Is this instance clustered, and which node is it running on?
SELECT SERVERPROPERTY('IsClustered') AS is_clustered,
SERVERPROPERTY('ComputerNamePhysicalNetBIOS') AS current_node;
-- Cluster nodes and their state
SELECT NodeName, status_description, is_current_owner
FROM sys.dm_os_cluster_nodes;
-- Shared drives the instance can use
SELECT DriveName FROM sys.dm_io_cluster_shared_drives;
Moving the instance to another node (change: needs approval) is done in Failover Cluster Manager, or in PowerShell on a cluster node:
Get-ClusterGroup
Move-ClusterGroup -Name 'SQL Server (MSSQLSERVER)' -Node 'NODE2'
Patch an FCI one node at a time: patch the passive node, move the instance to it, then patch the node you left.
Distributed Availability Groups
A distributed AG links two separate AGs, usually in different clusters or data centres. The primary AG sends its log to the other AG’s primary (the forwarder), which passes it on to its own secondaries. It suits disaster recovery sites and migrations to new hardware or a new Windows version.
Step 1, on the primary replica of AG1:
CREATE AVAILABILITY GROUP [DistAG]
WITH (DISTRIBUTED)
AVAILABILITY GROUP ON
N'AG1' WITH (LISTENER_URL = N'TCP://ag1-lsn.domain.local:5022',
AVAILABILITY_MODE = ASYNCHRONOUS_COMMIT,
FAILOVER_MODE = MANUAL,
SEEDING_MODE = AUTOMATIC),
N'AG2' WITH (LISTENER_URL = N'TCP://ag2-lsn.domain.local:5022',
AVAILABILITY_MODE = ASYNCHRONOUS_COMMIT,
FAILOVER_MODE = MANUAL,
SEEDING_MODE = AUTOMATIC);
LISTENER_URL uses each AG’s listener name with the endpoint port (5022), not the SQL port.
Step 2, on the primary replica of AG2:
ALTER AVAILABILITY GROUP [DistAG]
JOIN
AVAILABILITY GROUP ON
N'AG1' WITH (LISTENER_URL = N'TCP://ag1-lsn.domain.local:5022',
AVAILABILITY_MODE = ASYNCHRONOUS_COMMIT,
FAILOVER_MODE = MANUAL,
SEEDING_MODE = AUTOMATIC),
N'AG2' WITH (LISTENER_URL = N'TCP://ag2-lsn.domain.local:5022',
AVAILABILITY_MODE = ASYNCHRONOUS_COMMIT,
FAILOVER_MODE = MANUAL,
SEEDING_MODE = AUTOMATIC);
Health check:
SELECT ag.name AS distributed_ag, ar.replica_server_name AS member_ag,
drs.synchronization_state_desc, drs.log_send_queue_size, drs.redo_queue_size
FROM sys.availability_groups ag
JOIN sys.availability_replicas ar ON ar.group_id = ag.group_id
LEFT JOIN sys.dm_hadr_database_replica_states drs ON drs.replica_id = ar.replica_id
WHERE ag.is_distributed = 1;
Failing over a distributed AG is always manual and has several steps: switch both members to synchronous commit, wait until they are synchronised with matching last hardened LSNs, set the old primary AG to the secondary role, then fail over on the new one. Follow Microsoft’s current procedure step by step, because getting the order wrong can lose data.
From my SQL Server DBA handbook.