powershelldba.de · Uwe Janke

gMSA Passwords: Generation, Rotation, Retrieval, and the Golden gMSA Risk

The selling point of a group Managed Service Account is that nobody ever knows its password. That is true, and it is also the part most teams never look into. The password still exists, it is still computed somewhere, it is still handed to a machine over the wire, and exactly one secret in your forest decides who else could compute it.

The Password Nobody Types

A gMSA is an Active Directory object whose password is not set by an administrator and not stored in a vault. It is derived on demand by a domain controller, handed to explicitly authorized hosts, and replaced on a fixed interval without anyone being told. In SQL Server Configuration Manager you enter CORP\gmsa-sql01$ and leave the password field empty. That empty field is the entire point: there is no value to leak in a runbook, no value to paste into a ticket, and no value that goes stale in a password manager when it rotates.

What people usually want to know, once a gMSA is actually running a production instance, is what happens on day 30, what happens when a domain controller is unreachable, and who exactly is able to read the thing. Those three questions have precise answers.

Where the Password Comes From

gMSA passwords are not stored as passwords. They are computed from a forest-wide secret called the KDS root key, using the Microsoft Group Key Distribution Service. The inputs to the derivation are:

Same inputs, same output. Any domain controller can produce the current password for any gMSA without that password being stored or replicated anywhere, which is why gMSA passwords do not create the replication and reset headaches ordinary service account passwords do. The derived value is several hundred bytes of key material, far past anything a password policy could reasonably require and far past anything a human would ever transcribe. Its exact length is an implementation detail. The relevant property is that nobody ever handles it.

The KDS root key is created once per forest:

# Production: create the root key and wait
Add-KdsRootKey -EffectiveImmediately

# Lab only: skip the wait by backdating the effective time
Add-KdsRootKey -EffectiveTime ((Get-Date).AddHours(-10))

-EffectiveImmediately is the most misleading parameter name in this area. The key becomes usable roughly ten hours later, not immediately, and the delay is deliberate: it gives the key time to replicate to every domain controller in the forest, so a host that happens to talk to a different DC does not get a "no key" failure. Backdating the effective time is fine in a lab. In production, a backdated key can be handed out by a DC that has not yet received it, which is exactly the failure the wait exists to prevent.

Rotation: Every 30 Days, Without a Restart

The rotation interval is set at creation time via -ManagedPasswordIntervalInDays and defaults to 30. It is fixed for the life of the account. There is no supported way to change the interval on an existing gMSA, so if 30 days does not fit your control requirement, decide that before the account is created. Changing it later means creating a new account and moving the service to it.

When a host retrieves the managed password, the domain controller does not return a single value. It returns a structure containing the current password, the previous password once one exists, and two timing hints: how long the current password stays valid, and when the client should ask again. That is what makes rotation non-disruptive. During the changeover both the current and the previous password authenticate, so a host that has not refreshed yet is not locked out, and a ticket issued just before the change does not become invalid mid-request.

The Local Security Authority on each host handles the refresh. For SQL Server on a supported combination that means no service restart, no maintenance window, and no scheduled task. The service keeps running across a rotation it never observes.

⚠ The exception worth knowing: this only works for services that authenticate through the LSA using the account identity. Anything that reads a credential once at startup and caches it inside its own process, or any third-party agent that expects a literal password string in a configuration file, does not benefit. Before moving such a service to a gMSA, confirm it supports managed service accounts explicitly, rather than assuming it will behave the way the SQL Server engine does.

Who Is Allowed to Read It

Retrieval is not open to the domain. The gMSA object carries a security descriptor, exposed through PrincipalsAllowedToRetrieveManagedPassword and stored in msDS-GroupMSAMembership, listing exactly which computers or groups may ask for the password. Everything else gets nothing, including domain-joined machines that would otherwise be perfectly trusted.

Three practical consequences follow, and each accounts for a sizeable share of "the service will not start" calls:

Setting One Up for SQL Server

Group managed service accounts are supported for standalone SQL Server from SQL Server 2014 on Windows Server 2012 R2 and later, and for failover cluster instances from SQL Server 2016. The cluster case is the one that actually justifies the "group" in the name: every node runs the service under the same account, so there is no per-node credential to keep aligned.

# On a machine with the AD PowerShell module, once per account
New-ADServiceAccount -Name 'gmsa-sql01' `
    -DNSHostName 'gmsa-sql01.corp.example.com' `
    -PrincipalsAllowedToRetrieveManagedPassword 'SQL-Hosts' `
    -ManagedPasswordIntervalInDays 30 `
    -ServicePrincipalNames 'MSSQLSvc/sql01.corp.example.com:1433',
                           'MSSQLSvc/sql01.corp.example.com'

The account name is limited to 15 characters, and the resulting sAMAccountName carries a trailing $. That dollar sign is part of the name you type into Configuration Manager, and leaving it off produces a logon failure that looks nothing like a naming problem.

On each host that will run the service:

Install-ADServiceAccount -Identity 'gmsa-sql01'
Test-ADServiceAccount    -Identity 'gmsa-sql01'   # must return True before you touch SQL Server

Test-ADServiceAccount is the gate. It answers exactly one question, "can this machine retrieve the password right now", and it answers it without involving SQL Server at all. If it returns False, nothing you do in Configuration Manager will help, and every minute spent looking at SQL Server is a minute spent in the wrong place.

Then set the service account in SQL Server Configuration Manager, not in services.msc, which does not grant the rights the engine expects. Enter CORP\gmsa-sql01$ and leave the password fields empty. Configuration Manager grants "Log on as a service" and the remaining privileges.

Verify what the instance actually runs under. The service account SQL Server reports is authoritative, whatever the change ticket says:
SELECT servicename, service_account, startup_type_desc, status_desc
FROM sys.dm_server_services;

What the Usual Failures Actually Mean

Symptom Actual cause Check
Service fails to start, logon failure in the System log Host cannot retrieve the password, or the trailing $ is missing Test-ADServiceAccount on the host
Test-ADServiceAccount returns False on a new host Host added to the allowed group but not rebooted Reboot, then retest
Fails on every host in a brand new environment KDS root key missing or not yet effective Get-KdsRootKey and its effective time
Works at HQ, fails at a branch site Only a read-only DC reachable, or AD traffic filtered Confirm a writable DC is reachable from the host
Intermittent Kerberos failures after a while Clock skew beyond the Kerberos tolerance Time sync against the domain hierarchy
Connections fall back to NTLM, delegation breaks SPNs still registered on the old service account SPN report against the gMSA

That last row deserves a note. Moving a SQL Server service from a domain user account to a gMSA moves the identity the MSSQLSvc SPNs must hang off. If the SPNs stay on the old account, Kerberos stops working for that instance and everything quietly degrades to NTLM, which usually surfaces much later as a double-hop or linked server problem rather than as an obvious authentication error. Register the SPNs on the gMSA and remove the stale ones from the old account.

Where a gMSA Cannot Help You

The absence of a password is a feature right up to the point where something requires one. The cases that come up in SQL Server work:

The Golden gMSA Problem

This is the part that belongs in a risk assessment and is usually missing from one. Because gMSA passwords are derived rather than stored, anyone who obtains the KDS root key material, plus the gMSA's SID and its password identifier, can compute that account's password themselves, offline.

⚠ Why this is worse than a stolen password: the computation happens offline, so it produces no password-retrieval event and no authentication trail on any domain controller. It is not limited to the current password, because future passwords derive from the same root key and a predictable time-based identifier. And rotation does not remediate it: the next password is just as computable as the current one.

The attack requires access that is already serious, root key material from a domain controller or from a system state backup of one, so this is not an argument against gMSA. Ordinary service accounts are worse in every respect. It is an argument about classification and monitoring:

For DBAs the practical takeaway is narrow: a gMSA behind your SQL Server estate is only as trustworthy as the domain controllers and their backups. That is generally a much better trade than a shared service account password in a spreadsheet, but it is a different risk, not an absent one.

A Note on Delegated MSAs

Windows Server 2025 adds delegated Managed Service Accounts, aimed squarely at the migration problem: taking an existing user-based service account and moving it to a machine-bound managed identity without the usual rebuild. The concept is promising for exactly those SQL Server service accounts that never got migrated because nobody wanted to touch them. Early security research on the migration attributes has also shown that write access to them is highly privileged in practice, so if you plan to adopt dMSAs, treat the delegation and migration attributes as tier-0 from day one rather than as ordinary object permissions.

Checking What You Have

Two commands worth keeping. What Active Directory says about the account:

Get-ADServiceAccount -Identity 'gmsa-sql01' -Properties `
    PrincipalsAllowedToRetrieveManagedPassword,
    'msDS-ManagedPasswordInterval',
    ServicePrincipalNames,
    PasswordLastSet |
    Format-List

And what your instances are actually running under, which is the number that matters when someone claims the estate was migrated:

Get-DbaService -ComputerName 'SQL01','SQL02','SQL03' -Type Engine, Agent |
    Select-Object ComputerName, ServiceName, StartName, State

For the SPN side of a gMSA migration, Get-sqmSpnReport from sqmSQLTool determines the service account per instance, including a gMSA, derives the expected MSSQLSvc SPNs for hostname, FQDN, named instances, and Availability Group listeners, and writes the missing ones out as ready-to-use setspn commands plus a comment-free block that can be handed straight to the AD team. That last part matters more than it sounds: on a gMSA migration the SPN work is usually done by a different team than the SQL work, and the handover is where it stalls. Full reference: sqmSQLTool commands.

Integration with Control Matrix

Our Control Matrix covers service account and authentication controls across SQL Server infrastructure in more depth. See also Kerberos and SPN: Windows Authentication Deep Dive for how SPN registration and delegation behave once the identity is in place, and Extended Protection for SQL Server for hardening the channel that identity authenticates over.

The Bottom Line

A gMSA password is derived from a forest secret, rotated on a fixed interval that cannot be changed after creation, handed only to hosts you named explicitly, and refreshed by the LSA without restarting SQL Server. Get three things right and it is close to maintenance-free: verify retrieval with Test-ADServiceAccount before touching SQL Server, move the SPNs to the new identity, and accept that Agent proxies still need a real credential.

Then classify the KDS root key for what it is. The whole design rests on it, and a password nobody knows stays unknown only as long as the key that produces it stays where it belongs.

← Back to Blog