How to Set Up SQL Server Always On Availability Groups on Windows Server 2025

Always On Availability Groups (AG) is SQL Server’s premier high-availability and disaster recovery solution, enabling automatic failover with near-zero data loss and readable secondary replicas that offload reporting workloads from the primary. On Windows Server 2025, the underpinning technology — Windows Server Failover Clustering (WSFC) — benefits from improved cluster health policies, enhanced direct migration for Hyper-V virtual machines, and better integration with Storage Spaces Direct. SQL Server 2022’s Always On also introduces cross-subnet multi-subnet listener improvements and contained availability groups that simplify database portability. This guide walks you through building a two-node Always On Availability Group from scratch: installing clustering prerequisites, creating the WSFC cluster, enabling Always On in SQL Server, configuring replicas, creating the AG with PowerShell and T-SQL, setting up a listener, and validating failover behavior.

Prerequisites

  • Two or more Windows Server 2025 servers (physical or virtual) joined to the same Active Directory domain.
  • SQL Server 2022 Enterprise or Developer edition installed identically on each node (same instance name, same collation, same features).
  • All nodes must be able to communicate on port 5022 (database mirroring endpoint) and port 1433 (SQL client connections).
  • A domain account or gMSA for SQL Server services — the same account on all nodes simplifies endpoint permissions.
  • Shared witness resource — a File Share Witness on a third server, or a Cloud Witness using Azure Blob Storage.
  • The SqlServer and FailoverClusters PowerShell modules available on all nodes.

Step 1 — Install Failover Clustering Features

Run the following on all nodes that will participate in the cluster:

# Install Failover Clustering and management tools on each node
Install-WindowsFeature -Name Failover-Clustering -IncludeManagementTools -Restart:$false

# Install RSAT for Failover Clustering (admin tools on management station)
Install-WindowsFeature -Name RSAT-Clustering, RSAT-Clustering-Mgmt, RSAT-Clustering-PowerShell

# Verify the feature is installed
Get-WindowsFeature -Name Failover-Clustering | Select-Object Name, Installed, InstallState

Step 2 — Validate Cluster Configuration

Always run the cluster validation wizard before creating the cluster. It identifies hardware, networking, or Active Directory issues that will cause cluster creation to fail.

# Run cluster validation (produces a detailed HTML report)
$nodes = @("SQL2025-N1", "SQL2025-N2")
Test-Cluster -Node $nodes -Include "Storage", "Network", "System Configuration" `
    -ReportName "C:ClusterValidationPreBuildValidation"

# View the report path
Write-Host "Review the validation report before proceeding"
Invoke-Item "C:ClusterValidationPreBuildValidation.htm"

Resolve any errors (not warnings) before continuing. Common issues include missing network redundancy and mismatched firmware versions.

Step 3 — Create the Windows Server Failover Cluster

# Create the cluster — NoStorage because SQL AG uses its own log shipping, not shared disks
New-Cluster -Name "SQLCLUSTER01" `
    -Node "SQL2025-N1", "SQL2025-N2" `
    -StaticAddress "192.168.10.50" `
    -NoStorage

# Verify cluster is online
Get-Cluster -Name "SQLCLUSTER01" | Select-Object Name, SharedVolumesRoot
Get-ClusterNode -Cluster "SQLCLUSTER01" | Select-Object Name, State

Step 4 — Configure Quorum

For a two-node cluster a witness is mandatory — without it losing one node takes the cluster offline. Use a File Share Witness or Cloud Witness:

# Option A: File Share Witness (on a third server — \fileserverSQLCLUSTER01-Witness)
Set-ClusterQuorum -Cluster "SQLCLUSTER01" `
    -FileShareWitness "\WIT2025SQLCLUSTER01-Witness"

# Option B: Cloud Witness (Azure Storage Account)
Set-ClusterQuorum -Cluster "SQLCLUSTER01" `
    -CloudWitness `
    -AccountName "sqlag2025witness" `
    -AccountKey "base64keystringfromazureportal=="

# Verify quorum configuration
Get-ClusterQuorum -Cluster "SQLCLUSTER01"

Step 5 — Enable Always On Availability Groups in SQL Server

Always On must be enabled on each SQL Server instance via SQL Server Configuration Manager or PowerShell:

# Import the SQL Server module
Import-Module SqlServer

# Enable Always On on the primary node
Enable-SqlAlwaysOn -ServerInstance "SQL2025-N1" -Force
# Enable on the secondary node
Enable-SqlAlwaysOn -ServerInstance "SQL2025-N2" -Force

# Verify Always On is enabled
Invoke-Sqlcmd -ServerInstance "SQL2025-N1" `
    -Query "SELECT is_hadr_enabled FROM sys.configurations WHERE name = 'hadr enabled'" `
    -TrustServerCertificate

SQL Server must be restarted after enabling Always On. The -Force parameter does this automatically.

Step 6 — Create the Database Mirroring Endpoint

Each replica communicates through a mirroring endpoint. Create it on all nodes:

-- Run on SQL2025-N1 (primary)
CREATE ENDPOINT [Hadr_endpoint]
    STATE = STARTED
    AS TCP (LISTENER_PORT = 5022, LISTENER_IP = ALL)
    FOR DATA_MIRRORING (ROLE = ALL, ENCRYPTION = REQUIRED ALGORITHM AES);
GO

-- Grant CONNECT permission to the SQL Server service account
GRANT CONNECT ON ENDPOINT::[Hadr_endpoint] TO [CORPsvc-sqlengine];
GO
-- Run identically on SQL2025-N2 (secondary)
CREATE ENDPOINT [Hadr_endpoint]
    STATE = STARTED
    AS TCP (LISTENER_PORT = 5022, LISTENER_IP = ALL)
    FOR DATA_MIRRORING (ROLE = ALL, ENCRYPTION = REQUIRED ALGORITHM AES);
GO

GRANT CONNECT ON ENDPOINT::[Hadr_endpoint] TO [CORPsvc-sqlengine];
GO

Add a firewall rule for the endpoint port on all nodes:

New-NetFirewallRule -DisplayName "SQL AG Hadr Endpoint" `
    -Direction Inbound -Protocol TCP -LocalPort 5022 `
    -Action Allow -Profile Domain

Step 7 — Prepare the Database for the Availability Group

The database must be in Full recovery model and have at least one full backup taken before joining an AG:

-- On SQL2025-N1 (primary)
USE [master];
GO

-- Set recovery model to Full
ALTER DATABASE [SalesDB] SET RECOVERY FULL;
GO

-- Take a full backup
BACKUP DATABASE [SalesDB]
    TO DISK = 'G:SQLBackupSalesDB_FullForAG.bak'
    WITH FORMAT, INIT, NAME = 'SalesDB-Full for AG', COMPRESSION;
GO

-- Take a transaction log backup
BACKUP LOG [SalesDB]
    TO DISK = 'G:SQLBackupSalesDB_LogForAG.trn'
    WITH FORMAT, INIT, NAME = 'SalesDB-Log for AG';
GO

Restore the backups on SQL2025-N2 with NORECOVERY to prepare it as a secondary:

-- On SQL2025-N2 (secondary) — restore with NORECOVERY
RESTORE DATABASE [SalesDB]
    FROM DISK = '\SQL2025-N1SQLBackupSalesDB_FullForAG.bak'
    WITH NORECOVERY, REPLACE,
    MOVE 'SalesDB' TO 'D:SQLDataSalesDB.mdf',
    MOVE 'SalesDB_log' TO 'E:SQLLogSalesDB_log.ldf';
GO

RESTORE LOG [SalesDB]
    FROM DISK = '\SQL2025-N1SQLBackupSalesDB_LogForAG.trn'
    WITH NORECOVERY;
GO

Step 8 — Create the Availability Group with PowerShell

Import-Module SqlServer

# Define the primary replica
$primaryReplica = New-SqlAvailabilityReplica `
    -Name "SQL2025-N1" `
    -EndpointURL "TCP://SQL2025-N1.corp.local:5022" `
    -AvailabilityMode SynchronousCommit `
    -FailoverMode Automatic `
    -ConnectionModeInSecondaryRole AllowAllConnections `
    -AsTemplate -Version 16

# Define the secondary replica
$secondaryReplica = New-SqlAvailabilityReplica `
    -Name "SQL2025-N2" `
    -EndpointURL "TCP://SQL2025-N2.corp.local:5022" `
    -AvailabilityMode SynchronousCommit `
    -FailoverMode Automatic `
    -ConnectionModeInSecondaryRole AllowReadIntentConnectionsOnly `
    -AsTemplate -Version 16

# Create the Availability Group
New-SqlAvailabilityGroup `
    -InputObject "SQL2025-N1" `
    -Name "AG_Sales" `
    -AvailabilityReplica @($primaryReplica, $secondaryReplica) `
    -Database "SalesDB" `
    -BasicAvailabilityGroup:$false `
    -DatabaseHealthTrigger:$true

# Join the secondary to the AG
Join-SqlAvailabilityGroup -Path "SQLSERVER:SQLSQL2025-N2DEFAULT" -Name "AG_Sales"

# Add the database to the secondary
Add-SqlAvailabilityDatabase -Path "SQLSERVER:SQLSQL2025-N2DEFAULTAvailabilityGroupsAG_Sales" `
    -Database "SalesDB"

Step 9 — Create an Availability Group Listener

The AG listener is a virtual network name (VNN) that clients connect to, regardless of which replica is currently primary:

# Create listener with a static IP
Add-SqlAvailabilityGroupListenerStaticIP `
    -Path "SQLSERVER:SQLSQL2025-N1DEFAULTAvailabilityGroupsAG_Sales" `
    -ListenerName "AG_Sales_Listener" `
    -Port 1433 `
    -StaticIP @("192.168.10.55/255.255.255.0")
-- Verify listener from T-SQL
SELECT dns_name, port, ip_configuration_string_from_cluster
FROM sys.availability_group_listeners
WHERE group_id = (SELECT group_id FROM sys.availability_groups WHERE name = 'AG_Sales');

Step 10 — Test Failover

# Check current primary
Get-SqlAvailabilityGroup -ServerInstance "SQL2025-N1" -Name "AG_Sales" |
    Select-Object -ExpandProperty AvailabilityReplicas |
    Select-Object Name, Role, AvailabilityMode, FailoverMode

# Perform a planned manual failover to the secondary
Switch-SqlAvailabilityGroup `
    -Path "SQLSERVER:SQLSQL2025-N2DEFAULTAvailabilityGroupsAG_Sales"

# Confirm the new primary
Get-SqlAvailabilityGroup -ServerInstance "SQL2025-N2" -Name "AG_Sales" |
    Select-Object -ExpandProperty AvailabilityReplicas |
    Select-Object Name, Role

Monitor the AG health dashboard in SSMS: right-click the AG in Object Explorer and choose Show Dashboard. Synchronization state should read Synchronized and failover readiness No data loss.

Conclusion

A properly configured Always On Availability Group on Windows Server 2025 with SQL Server 2022 provides automatic failover within seconds, protecting your SalesDB from instance-level failures with synchronous commit and near-zero RPO. The secondary replica, configured with AllowReadIntentConnectionsOnly, can serve read-scale workloads by pointing application connection strings to the listener with ApplicationIntent=ReadOnly. You can also redirect secondary replica backups to reduce I/O load on the primary — configure this via the AG backup preference in SSMS or via ALTER AVAILABILITY GROUP ... SET (BACKUP_PRIORITY = 60) on the secondary replica. With the cluster validated, the endpoint secured with AES encryption, and quorum properly configured, your AG environment is ready for production traffic.