DBA Tooling · FI-TS Munich · 2026

SQL Server AlwaysOn
Setup Tool

Fully automatic configuration of Availability Groups
on Windows Server Failover Clusters

SQL Server 2022 / 2025 Windows Server 2022 PowerShell 5.1 WinForms GUI v1.0.0

Uwe Janke  ·  Senior SQL Server DBA  ·  dtcSoftware

Motivation

The Problem

Manual AlwaysOn configuration - an error-prone marathon.

⏱️

2-4 hours

Time spent on a manual AG setup - depending on the operator's experience

📋

Checklist maintenance

Every DBA keeps their own Word checklist. No consistent standard, no versioning

🔄

Not reproducible

Forgot a step? Wrong port? Endpoint already exists? - Errors surface late

🔐

Kerberos / SPN trap

SPNs missing → Cannot generate SSPI context → contact the AD team → wait → continue

🧹

Cleanup after a failed attempt

The WSFC group still hangs around, a registry key blocks a retry - hard to find without experience

📄

No documentation

What was configured? Which failover mode? Which service account? - Nearly impossible to reconstruct afterward

Overview

The Solution

A single script - AlwaysOnSetup.ps1 - handles all 9 configuration steps fully automatically after a one-time parameter check.


🖥️

WinForms GUI

Cluster data is read in automatically. No typing needed - just review and start.

📝

Automatic documentation

Cluster backup, a full log, and an SPN request file are saved automatically.

🔁

Idempotent & robust

Can be restarted after a failure. Cleans up WSFC leftovers automatically.

BeforeAfter
2-4 hours~20 minutes
Word/Excel checklistGUI with pre-filled values
No log3 files automatically
SPN: call the AD teamsetspn file generated
Failed attempt → manual cleanupAutomatic WSFC cleanup
✓ Tested on Windows Server 2022 · SQL Server 2022 Enterprise & Standard · 2- and 3-node clusters
Preparation

Prerequisites

The tool configures - it doesn't build. What needs to be in place beforehand:

Infrastructure

WSFC already set up

Windows Server Failover Cluster with 2 or 3 nodes

SQL Server installed

Enterprise or Standard on all nodes, matching version

Port 5022 open

Between all nodes for the HADR mirroring endpoint

Backup share reachable

UNC path writable from all nodes

Permissions & modules

RequirementNote
Local administratorOn the executing node
SQL sysadminOn all nodes
Domain read accessFor SPN checking (step 9)
FailoverClustersInstalled automatically
dbatools ≥ 2.0Installed automatically
⚡ Recommendation Set SPNs before the setup - saves a manual intermediate step. Details on slide 9.
Operation

The Interface

Cluster data is read in automatically. PropertyGrid on the left - live log on the right.

🔷 SQL Server AlwaysOn Setup Tool v1.0.0
Configuration
① CONFIGURATION (PropertyGrid)
1 - Cluster
Cluster name
SQLCLUSTER01
Listener name
AG-LISTENER01
Listener IP
10.0.1.50
Listener port
1433
2 - Nodes
Node 1
▶ SQLNODE01\MSSQLSERVER
SQL instance
SQLNODE01\MSSQLSERVER
Hostname
SQLNODE01
AlwaysOn status
Disabled
Node 2
▶ SQLNODE02\MSSQLSERVER
Node 3
(empty)
3 - Availability Group
AG name
AG_PROD_01
Endpoint port
5022
Failover mode
Automatic ▾
Backup preference
Primary ▾
Test database
AG_TestDB
4 - SQL service account
Service account
HLB\svc-sql-prod
Password
••••••••••••
⑤ LOG (live)
[10:14:22] === Reading cluster and SQL information ===
[10:14:22] Cluster found: SQLCLUSTER01
[10:14:23] Nodes: SQLNODE01, SQLNODE02
[10:14:23] Listener: AG-LISTENER01 IP: 10.0.1.50 Port: 1433
[10:14:24] Read: SQLCLUSTER01 - OK
 
[10:15:01] === Step 2: Enable HADR ===
[10:15:03] SQLNODE01: HADR enabled - restarting the service
[10:15:14] SQLNODE01: SQL Server reachable again
[10:15:14] SQLNODE02: HADR enabled - restarting the service
[10:15:22] === Step 3: Create endpoint ===
[10:15:23] SQLNODE01: Endpoint 'Hadr_endpoint' (5022) created
[10:15:23] SQLNODE02: Endpoint 'Hadr_endpoint' (5022) created
[10:15:24] === Step 6: Create Availability Group ===
[10:15:31] AG_PROD_01 created successfully ✓
[10:15:32] SPN MSSQLSvc/SQLNODE02:1433 missing - please inform the AD team
⑦ AG_PROD_01 configured successfully SPN file: C:\System\WinSrvLog\MSSQL\AlwaysOn_SPN_ADTeam_20260513.txt
Workflow

9-Step Workflow

All steps run sequentially and are logged. Skipped steps are noted in the log.

1
Service account
Optional · WMI
2
Enable HADR
sp_configure
3
Endpoint
Port 5022
4
CONNECT
GRANT
5
Test DB
FULL + backup
6
Create AG
T-SQL / sqlcmd
7
Listener
ALTER AG
8
Status
DMV check
9
SPN check
AD team file
ℹ Step 1 is optional If no service account change is needed, step 1 is skipped. The tool detects the current account state automatically.
⚠ Can pause at step 4 If Kerberos auth fails (missing SPNs), the tool pauses and prompts for a manual SQL login creation.
Deep Dive · Step 2

HADR Activation

AlwaysOn is disabled by default in SQL Server and must be turned on individually on every node.

1
Set sp_configure

On all nodes: hadr enabled = 1 via Invoke-DbaQuery

2
Service restart via WMI

Win32_Service.StopService() / StartService() - no Restart-Computer needed

3
Readiness test

SELECT 1 every 2 sec., max. 120 sec. - only then does it move on to step 3

ℹ Why WMI instead of Restart-Service? Restart-Service MSSQLSERVER doesn't check whether the service is actually ready again. WMI plus an active connection test ensures SQL Server is fully initialized before step 3 begins.
# On every node:
Invoke-DbaQuery -SqlInstance $node `
  -Query "EXEC sp_configure 'hadr enabled',1;
           RECONFIGURE"

# WMI service restart
$svc = Get-WmiObject Win32_Service `
  -ComputerName $node `
  -Filter "Name='MSSQLSERVER'"
$svc.StopService()  # wait until Stopped
$svc.StartService() # wait until Running

# Readiness test (max. 120s)
$ok = $false
for ($i=0; $i -lt 60; $i++) {
  try {
    Invoke-DbaQuery -Query "SELECT 1"
    $ok = $true; break
  } catch { Start-Sleep 2 }
}
if (-not $ok) { throw "Timeout" }
Deep Dive · Step 6

AG Creation - Why sqlcmd?

For CREATE AVAILABILITY GROUP, sqlcmd is used deliberately - not Invoke-DbaQuery.

⚠ The dbatools connection-cache problem After the service restart in step 2, dbatools holds cached connection objects internally. Through those, sys.availability_groups can still appear empty - even though the AG already exists. sqlcmd opens a fresh TCP connection on every call and sees the current state.
ℹ dbatools - deliberately minimal use Only Invoke-DbaQuery and Connect-DbaInstance are used. Every other operation: direct T-SQL, WMI, or FailoverClusters cmdlets.
# CREATE AVAILABILITY GROUP via sqlcmd
# -- fresh connection, no dbatools cache
$sql = @"
CREATE AVAILABILITY GROUP [$AgName]
WITH (AUTOMATED_BACKUP_PREFERENCE = PRIMARY)
FOR DATABASE [$TestDb]
REPLICA ON
  N'$node1' WITH (
    ENDPOINT_URL = 'TCP://$node1:5022',
    FAILOVER_MODE = AUTOMATIC,
    AVAILABILITY_MODE = SYNCHRONOUS_COMMIT),
  N'$node2' WITH (
    ENDPOINT_URL = 'TCP://$node2:5022',
    FAILOVER_MODE = AUTOMATIC,
    AVAILABILITY_MODE = SYNCHRONOUS_COMMIT);
"@

# Call sqlcmd directly
sqlcmd -S $node1 -E -Q $sql
Deep Dive · Step 4 + 9

Kerberos & SPN Handling

SPNs enable Kerberos auth. If they're missing, Windows auth fails with Cannot generate SSPI context.

SituationFlow
SPNs set ✓Fully automatic - no intervention
SPNs missing ✗Pause → show T-SQL → manual → continue

Fallback mechanism:

1
Tool detects SSPI error

Kerberos test fails on the affected node

2
T-SQL block in the log

Temporary login - ready to copy into SSMS

3
Run manually + continue

Tool checks SQL auth, then continues with all steps

4
Automatic cleanup

Temporary login is guaranteed to be removed at the end

Required SPNs

# Per node - short name and FQDN
setspn -S MSSQLSvc/SQLNODE01:1433 DOM\svc-sql
setspn -S MSSQLSvc/SQLNODE01.dom.com:1433 DOM\svc-sql

setspn -S MSSQLSvc/SQLNODE02:1433 DOM\svc-sql
setspn -S MSSQLSvc/SQLNODE02.dom.com:1433 DOM\svc-sql

# AG listener
setspn -S MSSQLSvc/AG-LISTENER:1433 DOM\svc-sql
setspn -S MSSQLSvc/AG-LISTENER.dom.com:1433 DOM\svc-sql

# Verification:
setspn -L DOM\svc-sql
✓ Step 9 generates these commands automatically File: AlwaysOn_SPN_ADTeam_<date>.txt
Simply hand it off to the AD team.
Outputs

Output & Logging

All files land automatically in C:\System\WinSrvLog\MSSQL\ - no manual action needed.

FileContentTiming
AlwaysOn_ClusterSettings_<date>.txtCluster backup before changesStep 1
AlwaysOn_Setup_<date>.logFull text logCompletion
AlwaysOn_Setup_<date>.rtfColored log (manual)Manual
AlwaysOn_SPN_ADTeam_<date>.txtsetspn commands for the AD teamStep 9

Log color coding

=== Step 6: Create Availability Group ===
[10:15:25] CREATE AVAILABILITY GROUP [AG_PROD_01] ...
[10:15:31] AG_PROD_01 created successfully ✓
[10:15:32] ⚠ SPN MSSQLSvc/SQLNODE02:1433 missing
[10:15:45] ✗ Endpoint port 5022 already in use
BlueSection header
GreenSuccess
YellowWarning / note
RedError
Robustness

WSFC Cleanup & Retry

Failed setup attempts leave WSFC leftovers behind that block a retry.


1
Check before creating the AG

Tool checks whether a WSFC group for the AG name already exists

2
Stop + Remove-ClusterGroup

-RemoveResources -Force - full cleanup

3
Registry cleanup

HadrAgNameToldMap on all nodes via Invoke-Command

4
Clean retry possible

No manual intervention needed

# Check and clean up WSFC leftovers
$existingGroup = Get-ClusterGroup `
  -Name $AgName -ErrorAction SilentlyContinue

if ($existingGroup) {
  Write-Log "Orphaned WSFC group found - cleaning up..."
  Stop-ClusterGroup -Name $AgName -Force
  Remove-ClusterGroup -Name $AgName `
    -RemoveResources -Force
}

# Clean up the registry on all nodes
$regKey = "HKLM:\Cluster\HadrAgNameToldMap"
Invoke-Command -ComputerName $allNodes {
  if (Test-Path $using:regKey) {
    Remove-ItemProperty -Path $using:regKey `
      -Name $using:AgName -ErrorAction SilentlyContinue
  }
}
Toolchain

Connection to sqmSQLTool

The AlwaysOn Setup Tool sets up the AG. sqmSQLTool then takes over day-to-day operations.

After setup - sqmSQLTool tasks

🏥

Set up monitoring

Invoke-sqmSplunkConfiguration - AG replication status, sync health, failover events sent to Splunk

🔐

SA obfuscation

Invoke-sqmSaObfuscation - rename the SA account on all nodes, reduce the default attack surface

📜

Create a certificate

New-sqmSqlCertificate - secure endpoint encryption with your own SQL certificate

Ongoing operations

🔍

AlwaysOn health check

Get-sqmServerSetting - regular check of every AG replica's synchronization status

💾

Backup management

Invoke-sqmUserDatabaseBackup - detects read-only secondaries automatically and skips them

🐛

Deadlock analysis

Get-sqmDeadlockReport - AG-aware, reads only from the primary, hourly capture

sqmSQLTool - next session SQLSetupTool - AG after a fresh install DeadlockCollector - ongoing operations
Boundaries

Scope & Boundaries

What the tool deliberately does not do - and why.

Outside the scope

TaskResponsibility
Set up WSFCServer team / failover cluster setup
DNS records for the listenerNetwork team
Firewall port 5022Network team
Set SPNsAD team (the tool provides the commands)
Install SQL ServerSQLSetupTool
Add production databases to the AGManual / migration tool

Design decisions

ℹ Only one test database The tool adds one test DB to the AG - to prove it's working. Production databases are added afterward manually or via the SQLMigration tool.
ℹ No GUI autostart The script is started via PowerShell (.\AlwaysOnSetup.ps1). No installer, no registry entries - deliberately simple to deploy.
✓ Every change is reversible Cluster backup before step 1. HADR can be disabled again. The temporary login is always removed.
Error Handling

Troubleshooting

Error messageCauseSolution
Cannot generate SSPI context SPNs missing in AD Perform the manual SQL login step in the tool. Long-term: have the AD team set the SPNs
WSFC group ... already exists Leftover from a failed attempt Tool cleans up automatically: Remove-ClusterGroup + registry
Login failed for user AGSetup_... Mixed mode disabled or password policy Server Properties → Security → SQL Server and Windows Authentication mode
Backup directory not reachable UNC path invalid / no write permissions Correct the path in the PropertyGrid - must be writable from all nodes
Endpoint port 5022 in use Existing endpoint or another service SELECT * FROM sys.endpoints WHERE type=4 - adjust the port in the PropertyGrid
AG not visible after 60s SQL Server not fully ready Tool actively waits up to 120s. Check the service manually afterward
Invalid endpoint owner The temporary AGSetup_* login was the owner and was deleted AUTHORIZATION is set to the service account - no longer occurs
✓ Endpoint owner - how it works Without an AUTHORIZATION clause, SQL Server sets the currently executing login as the endpoint owner. In the SQL-auth fallback (missing SPNs), that would be the temporary AGSetup_XXXXXXXX login - which is deleted at the end of step 9. Result: the endpoint still exists, but its owning principal no longer does. The tool therefore explicitly sets AUTHORIZATION [<ServiceAccount>] - or sa as a fallback.
Summary

Summary & Next Steps

Key Takeaways

2-4h → ~20 minutes

Fully automatic run after a one-time parameter check

Built-in Kerberos fallback

Missing SPNs don't stop the setup - a controlled workaround

Automatic documentation

Cluster backup, log, SPN file - with no extra effort

Idempotent

WSFC cleanup allows a clean retry after a failure

Part of the toolchain

Together with sqmSQLTool and SQLSetupTool, a complete DBA workflow

Links & Resources

GitHubgithub.com/JankeUwe/AlwaysOnSetup
DocumentationAlwaysOn_Doku.html
HandbookAlwaysOn_Handbuch.docx
Websitewww.powershelldba.de

Next presentations

SQLSetupTool - Standardized SQL installation sqmSQLTool - DBA toolbox (monitoring, backup, analysis) DeadlockCollector - Automatic deadlock capture ReportDeploymentTool - SSRS mass deployment