Put two or more Exchange servers behind a load balancer and domain-joined Outlook clients quietly end up on NTLM instead of Kerberos. They connect to mail.contoso.com, no account in Active Directory owns that name, and Negotiate falls back to NTLM. In this post I'll set up the alternate service account (ASA) the way Microsoft documents it for Exchange Server SE, 2019 and 2016, and show how to prove that Kerberos is really in use.
How it works#
A Kerberos client asks a domain controller for a ticket to a service principal name (SPN) built from the host name it connects to, such as http/mail.contoso.com. The ticket is encrypted with the key of the account that owns that SPN, so only that account can read it.
- A single server's computer account covers its own name. A shared load-balanced name belongs to no account, so the client gets no ticket and Negotiate falls back to NTLM.
- Registering the shared SPN on one server's computer account doesn't help: a ticket encrypted for EX01 can't be decrypted when the load balancer sends the connection to EX02.
- The ASA fixes both problems. One Active Directory account owns the shared SPNs, and every Exchange server behind the namespace holds that account's password. The Microsoft Exchange Service Host service (
MSExchangeServiceHost) manages the credential on each server; if it isn't running, Kerberos doesn't work.
NTLM keeps working, so nothing looks broken. But each NTLM logon is passed through to a domain controller, which can become a bottleneck in large deployments, and Kerberos is the stronger protocol.
Prerequisites#
- The names your internal Outlook clients use, typically
mail.contoso.comandautodiscover.contoso.com. Microsoft supports CNAME records for shared names but recommends A records, so clients request the ticket for the shared name rather than a server FQDN. - All servers that share the same namespaces must use the same ASA credential. One account per forest is usually enough; if each Active Directory site has its own namespace, plan separate SPNs per site.
- Use a computer account, which Microsoft recommends because it doesn't allow interactive logon. Group managed service accounts (gMSA) aren't supported for this.
- Rights to create a computer account and register SPNs, the Active Directory PowerShell module, and Exchange Management Shell.
Step-by-step#
1. Create the ASA computer account#
Import-Module ActiveDirectory
New-ADComputer -Name EXASA -SamAccountName EXASA -Enabled:$true `
-Description 'Alternate Service Account credentials for Exchange' `
-AccountPassword (Read-Host 'Enter password' -AsSecureString)
Set-ADComputer EXASA -Add @{'msDS-SupportedEncryptionTypes' = '28'}The value 28 enables RC4-HMAC, AES128 and AES256 for the account. The account needs nothing beyond its default Domain Computers membership. Wait until it has replicated to all domain controllers.
2. Deploy the credential to the first server#
The RollAlternateServiceAccountPassword.ps1 script in the Exchange Scripts folder is the only supported way to deploy the ASA credential. With -GenerateNewPasswordFor, it sets a new password on the account in Active Directory and pushes it to the server:
Set-Location $exscripts
.\RollAlternateServiceAccountPassword.ps1 -ToSpecificServer ex01.contoso.com -GenerateNewPasswordFor 'CONTOSO\EXASA$'Answer Yes when it asks to change the password in Active Directory, and look for THE SCRIPT HAS SUCCEEDED at the end.
3. Copy it to every other server behind the namespace#
.\RollAlternateServiceAccountPassword.ps1 -ToSpecificServer ex02.contoso.com -CopyFrom ex01.contoso.com
Get-ClientAccessService -IncludeAlternateServiceAccountCredentialStatus |
Format-List Name, AlternateServiceAccountConfigurationGood looks like Latest: <date>, CONTOSO\EXASA$ on every server. Older documentation shows Get-ClientAccessServer, the cmdlet's previous name.
4. Register the SPNs on the ASA account#
Microsoft warns not to add the SPNs before the credential is on at least one server, or clients hit Kerberos errors. In practice finish step 3 on every server first, because a server without the credential can't decrypt the tickets. Then confirm nothing else owns the names, register them and list the result:
setspn -F -Q http/mail.contoso.com
setspn -F -Q http/autodiscover.contoso.com
setspn -S http/mail.contoso.com CONTOSO\EXASA$
setspn -S http/autodiscover.contoso.com CONTOSO\EXASA$
setspn -L CONTOSO\EXASA$The queries must find no existing registration: the ASA has to be the only account in the forest with these SPNs. One http/ SPN per name covers Outlook Anywhere, MAPI over HTTP, EWS, Autodiscover and the offline address book.
5. Offer Negotiate to Outlook#
Get-MapiVirtualDirectory | Format-List Server, IISAuthenticationMethods
Get-OutlookAnywhere | Format-List Server, InternalClientAuthenticationMethodMAPI over HTTP needs Negotiate in IISAuthenticationMethods, and Outlook Anywhere needs InternalClientAuthenticationMethod set to Negotiate. If Negotiate is missing and you use OAuth (hybrid with Exchange Online, or OAuth internally), add it to the existing list rather than replacing the list, as in Microsoft's example:
$mapidir = Get-MapiVirtualDirectory -Server EX01
$mapidir | Set-MapiVirtualDirectory -IISAuthenticationMethods ($mapidir.IISAuthenticationMethods += 'Negotiate')
Get-OutlookAnywhere -Server EX01 | Set-OutlookAnywhere -InternalClientAuthenticationMethod NegotiateWithout OAuth, Microsoft's form is Set-MapiVirtualDirectory -IISAuthenticationMethods Ntlm,Negotiate. Repeat for each server.
Verify#
On each server, Get-Service MSExchangeServiceHost must show Running. On a domain-joined client on the internal network, clear the ticket cache, restart Outlook and request the ticket yourself:
klist purge
klist get http/mail.contoso.comGood looks like a cached ticket with Server: http/mail.contoso.com @ CONTOSO.COM, ideally with KerbTicket Encryption Type: AES-256-CTS-HMAC-SHA1-96. If klist get fails, the SPN isn't registered, or isn't visible from the client's domain controller yet.
On the servers, Microsoft's check is the HttpProxy log: %ExchangeInstallPath%Logging\HttpProxy\Mapi for MAPI over HTTP and ...\HttpProxy\RpcHttp for Outlook Anywhere. An AuthenticationType of Negotiate means the server is accepting Kerberos connections:
$dir = Join-Path $env:ExchangeInstallPath 'Logging\HttpProxy\Mapi'
Get-ChildItem $dir -Filter *.log | Sort-Object LastWriteTime | Select-Object -Last 1 |
Select-String -Pattern ',Negotiate,' | Select-Object -Last 5Tips & gotchas#
- Roll the password. A computer-account password doesn't expire, but Microsoft recommends changing it periodically with the same procedure: generate a new password on one server, then copy it to the others straight away. A scheduled task works too, as long as you monitor it, because a failed rollover becomes an authentication outage.
- One owner per SPN. If a shared name is registered on two accounts, Kerberos fails for that name; if it sits on one server's computer account, the other servers can't decrypt the tickets.
setspn -Xreports duplicate SPNs. - Encryption types. Make sure AES is allowed on the account (value 28 includes it), especially if your domain restricts RC4, and check the ticket's encryption type with
klist. - Kerberos is for internal, domain-joined clients. Outlook outside the network keeps using whatever external authentication you publish; for modern authentication, see Hybrid Modern Authentication for Exchange Server.
- Turning it off. Remove the SPNs (
setspn -D http/mail.contoso.com CONTOSO\EXASA$), runSet-ClientAccessService EX01 -RemoveAlternateServiceAccountCredentialson each server, then delete the account. Clients set to Negotiate fall back to NTLM, clients configured for Kerberos only can't connect, and Microsoft suggests restarting client computers eventually to clear cached tickets.

