DBA Scripts: Check AG Replica Role and Synchronization State

🔧Part of the DBA-Tools Project, copy/paste SQL Server scripts and health checks.In: High Availability

Is Every Replica Actually Ready to Take Over

A failover just happened or the AG dashboard has gone red, and you need to know which replica is primary now and whether every secondary is connected and in sync. Run this on the primary: it reads each replica’s role, connection state, synchronization health and last connection error straight from the AG’s own DMVs.


Why Availability Group Replica State Matters

  • connected_state_desc and synchronization_health_desc are the two fastest signals for whether a secondary is actually protecting you, not just configured
  • A replica can stay listed in the AG topology while silently disconnected, last_connect_error_number and last_connect_error_timestamp are what expose that
  • Availability mode (synchronous vs asynchronous commit) changes what “healthy” should even look like, a synchronous replica that’s behind is a much bigger problem than an asynchronous one running a little behind
  • This is exactly the kind of check that’s easy to assume is fine because nothing has alerted, until the day a failover is needed and a replica turns out not to have been ready

When to Run This Script

  • Routine SQL Server health checks on any instance participating in an Availability Group
  • Immediately after any AG failover, planned or unplanned, to confirm every replica came back healthy
  • After a network change, patch, or maintenance window touching any replica
  • Before relying on a secondary for a planned failover or maintenance activity

The Script

Run the following script against your SQL Server instance.

✓ Verified
  • Tested on: SQL Server 2025 (RTM CU8) 17.0.4075.5, Windows lab instance, standalone with no availability group
  • Last verified: 2026-10-05 (saved output from a real run, the guard row, Get-AvailabilityGroupReplicaState-20261005-083053.csv). The replica query compiles on SQL Server 2025; its rows on a live AG follow MS Docs and were not reproduced here
  • Permissions: VIEW SERVER STATE, or VIEW SERVER PERFORMANCE STATE on SQL Server 2022 and later
  • Safety: read-only, impact low
/*
Script Name : Get-AvailabilityGroupReplicaState
Category    : high-availability
Purpose     : Show AG replica health, connection state, and synchronization status for failover readiness.
Author      : Peter Whyte (https://sqldba.blog/dba-scripts-check-ag-replica-role-and-synchronization-state/)
Requires    : VIEW SERVER STATE
HealthCheck : Yes
*/
-- SAFE:ReadOnly
-- IMPACT:Low
SET NOCOUNT ON;

IF SERVERPROPERTY('IsHadrEnabled') = 0
    OR NOT EXISTS (SELECT 1 FROM sys.availability_groups)
BEGIN
    SELECT 'Always On Availability Groups is not enabled or no groups are configured on this instance.' AS status;
END
ELSE
BEGIN
    SELECT
        ag.name                          AS ag_name,
        ar.replica_server_name,
        ar.availability_mode_desc        AS commit_mode,
        ar.failover_mode_desc,
        ars.role_desc,
        ars.operational_state_desc,
        ars.connected_state_desc,
        ars.synchronization_health_desc,
        ars.recovery_health_desc,
        ars.last_connect_error_number,
        ars.last_connect_error_description,
        ars.last_connect_error_timestamp
    FROM sys.availability_replicas                      AS ar
    JOIN sys.availability_groups                        AS ag  ON ag.group_id   = ar.group_id
    JOIN sys.dm_hadr_availability_replica_states        AS ars ON ars.replica_id = ar.replica_id
    ORDER BY ag.name, ar.replica_server_name;
END

Run it on the primary. On a secondary, sys.dm_hadr_availability_replica_states returns only the local replica, so every other replica drops out of the result. Where Always On is not enabled, or no availability group exists, the guard at the top returns one status row instead of an error, so it is safe to run across a mixed estate.


How To Run From The Repo

Clone DBA Tools, initialize and run the script:

# Clone dba-tools repo:
git clone https://github.com/peterwhyte-lgtm/dba-tools

# Initialize environment:
cd dba-tools
.\Initialize-Environment.ps1

# Check every AG replica's connection and sync health:
.\run.ps1 Get-AvailabilityGroupReplicaState

# To run against a remote sql server:
.\run.ps1 Get-AvailabilityGroupReplicaState -ServerInstance SQLSERVER01

This script lives in the repo at:


Example Output

With no availability group to report, the whole result is one status row:

Get-AvailabilityGroupReplicaState on the lab instanceexample output, not something to copy
status ------------------------------------------------------------------------------------------ Always On Availability Groups is not enabled or no groups are configured on this instance.

If you get this row on a server that should host a replica, check SERVERPROPERTY('IsHadrEnabled'). 0 means the Always On feature is off there. 1 means the feature is on but the login that ran the script can see no availability group, which is either no AG or a permissions gap (see the FAQ).


Understanding the Results

role_desc
operational_state_desc
PRIMARY, SECONDARY or RESOLVING. RESOLVING means the replica has no settled role, and its operational_state_desc says why: PENDING_FAILOVER while a failover is being processed, OFFLINE when the group has no primary, FAILED or FAILED_NO_QUORUM when the cluster cannot be read or the node has lost quorum. On the primary, the secondary rows read NULL in operational_state_desc and recovery_health_desc, because those are returned only for the local replica.Act when a replica stays RESOLVING, or the primary’s own row reads anything but ONLINE. FAILED_NO_QUORUM points at the cluster, not at SQL Server.
connected_state_desc
CONNECTED or DISCONNECTED. The primary tracks the connection to every secondary, and a secondary tracks only its connection to the primary. When a secondary disconnects, the primary marks its databases as not synchronized and waits for it to reconnect.Act when any row reads DISCONNECTED. Read last_connect_error_number on the same row, then check the endpoint with Mirroring Endpoint Health and Status.
synchronization_health_desc
A rollup of every database on the replica. HEALTHY means each one is in its target state: SYNCHRONIZED on a synchronous-commit replica, SYNCHRONIZING on an asynchronous one. PARTIALLY_HEALTHY means some are not. NOT_HEALTHY means at least one database is NOT SYNCHRONIZING.Act when it is not HEALTHY. Run Availability Group Latency to see which database and its synchronization_state_desc. Never force a failover to a replica whose database reads REVERTING or INITIALIZING: it cannot then be started as a primary database.
commit_mode
SYNCHRONOUS_COMMIT, ASYNCHRONOUS_COMMIT or CONFIGURATION_ONLY. The primary waits for a synchronous-commit secondary to harden the log before a commit completes, but only up to the session timeout (10 seconds by default). After that it marks the replica NOT_HEALTHY and stops waiting until it reconnects. An asynchronous-commit replica never makes the primary wait and never guarantees zero data loss.Act when a SYNCHRONOUS_COMMIT row is not HEALTHY. A failover to it is no longer zero data loss. If required_synchronized_secondaries_to_commit in sys.availability_groups is above 0, commits on the primary wait for that many synchronized secondaries.
last_connect_error_number
last_connect_error_timestamp
The last connection error recorded for the replica, with its message in last_connect_error_description and the time it happened.Act when the timestamp falls inside the incident window. That error is your lead. An older one on a CONNECTED replica is history.

Best Practices

  • Treat any DISCONNECTED or unhealthy synchronous replica as urgent: once the session timeout passes, the primary stops waiting for it, so a failover to it is no longer zero data loss
  • Check this immediately after every failover, planned or unplanned, don’t assume the old primary rejoined cleanly as a secondary
  • Pair with Availability Group Latency for the database-level detail behind any replica showing sync issues here

MS Docs: sys.dm_hadr_availability_replica_states · sys.dm_hadr_database_replica_states · sys.availability_groups · sys.availability_replicas · Availability modes


Related Scripts

You may also find these scripts useful:


Frequently Asked Questions

Why does the script return only one row on a secondary?

Because a secondary only knows about itself. sys.dm_hadr_availability_replica_states returns every replica only on the instance that hosts the primary, and local information alone on a secondary. Find the primary through the listener or the instance whose row reads PRIMARY, and run the script there.

What’s the difference between operational_state and connected_state?

connected_state_desc is about the connection between a secondary and the primary. operational_state_desc is about whether the replica is ready to serve its databases: PENDING, ONLINE or FAILED for a primary, ONLINE or FAILED for a secondary, and NULL on the primary for every secondary row.

Why did I get the “not enabled” row on a server that has an AG?

Either Always On is off there (SERVERPROPERTY('IsHadrEnabled') returns 0), you are connected to the wrong instance, or the login cannot see the group. MS Docs lists VIEW ANY DEFINITION for sys.availability_groups and sys.availability_replicas, the two views the guard and the join read, so rerun it as sysadmin or as a login with that permission before trusting the row. That last case is documented, not reproduced here.

What permission does it need?

VIEW SERVER STATE, or VIEW SERVER PERFORMANCE STATE on SQL Server 2022 and later. Without it the replica query fails with Msg 262, Level 14, State 1, “VIEW DATABASE PERFORMANCE STATE permission denied in database ‘master’.” Granting VIEW DATABASE PERFORMANCE STATE in master, as that message suggests, is not enough: the replica DMV then fails with Msg 300 asking for VIEW SERVER PERFORMANCE STATE.

Does a healthy result here guarantee a clean failover?

It confirms the replica is connected and synchronized, the two biggest failure modes, but a full failover readiness check also means confirming listener configuration, endpoint permissions, and (for asynchronous replicas) accepting the possibility of data loss. AG Failover Readiness and Readable Secondary Usage covers the per-database side. This script is the fast first check, not the entire runbook.


Summary

An Availability Group protects you only as far as its least healthy replica. Run this on the primary after every failover and in routine checks, and act on any row that is not CONNECTED and HEALTHY before a failover has to depend on it.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *