Changing AD FS service account

Changing AD FS service account isn't thing that you will usually need to perform. But sometimes due to initial poor configuration you have to do it. In our case AD FS service account was used in so many places... Many different users were using it in day to day routines. Several times password was resetted by people who weren't aware of this account usage in AD FS. After kerberos ticket expiration our AD FS was out of service and thus all services related on it. It was a real pain in the neck. You know it's a really frustrating when your cornerstone services work with issues and have low uptime.

It was decided to change service account to dedicated user <domain>\svc-adfs. Next steps were performed:

  • Proper permissions were set for databases (WID in our case)
  • Proper permissions were set for communication certificates
  • Proper permissions were set for certificate sharing container in AD
  • User was allowed to log in as a service
  • User was added to group 'Performance Log User'
  • adfssrv service was set running as new svc-adfs account
  • ADFSAppPool was set running as new svc-adfs account

But after doing all that neither IdpInitiatedSignOn page worked nor actual RPTs(Relying Party Trusts). Errors were as obscure as it could only be:

Encountered error during federation passive request. 

Additional Data 

Exception details: 
System.ServiceModel.FaultException: The server was unable to process the request due to an internal error.  For more information about the error, either turn on IncludeExceptionDetailInFaults (either from ServiceBehaviorAttribute or from the <serviceDebug> configuration behavior) on the server in order to send the exception information back to the client, or turn on tracing as per the Microsoft .NET Framework 3.0 SDK documentation and inspect the server trace logs.
   at Microsoft.IdentityServer.Protocols.Saml.Contract.MSISSamlProtocolContractClientManager.ProcessRequest(Message request)
   at Microsoft.IdentityServer.Protocols.Saml.Contract.MSISSamlProtocolContractClient.ProcessRequest(MSISSamlRequest samlRequest)
   at Microsoft.IdentityServer.Protocols.Saml.Contract.MSISSamlProtocolContractClient.ProcessRequest[T](MSISSamlRequest samlRequest)
   at Microsoft.IdentityServer.Protocols.Saml.Contract.MSISSamlProtocolContractClient.SignMessage(HttpSamlMessage httpSamlMessage, PrincipalType principalType, String principalIdentifier)
   at Microsoft.IdentityServer.Web.IdentityProviderInitiatedSignOn.BuildSignedSamlRequestMessage(HttpRedirectSamlBindingSerializer httpRedirectSamlBindingSerializer, AuthenticationRequest authenticationRequest, String relayState)
   at Microsoft.IdentityServer.Web.IdentityProviderInitiatedSignOn.SignOn(AuthenticationRequest authenticationRequest, String relayState)
   at Microsoft.IdentityServer.Web.IdentityProviderInitiatedSignOn.LocalIdentityProviderSignOn(Uri returnUrl, SignOnRequestParameters parameters)

and

The Federation Service could  not authorize token issuance for the caller '<domain>\svc-adfs
' on behalf of the subject '<domain>\user.name
' to the relying party 'https://.../'. Please see event 501 with the same instance id for caller identity. Please see event 502 with the same instance id for OnBehalfOf identity, if any. 

Additional Data 
Instance id:  
Exception details: 
Microsoft.IdentityServer.Service.IssuancePipeline.OnBehalfOfAuthorizationException: MSIS5009: The impersonation authorization failed for caller identity <domain>\svc-adfs and delegate <domain>\user.name for relying party trust https://.../.
   at Microsoft.IdentityModel.Threading.AsyncResult.End(IAsyncResult result)
   at Microsoft.IdentityModel.Protocols.WSTrust.WSTrustServiceContract.ProcessCoreAsyncResult.End(IAsyncResult ar)
   at Microsoft.IdentityModel.Protocols.WSTrust.WSTrustServiceContract.EndProcessCore(IAsyncResult ar, String requestAction, String responseAction, String trustNamespace) 
User Action 
Use Windows PowerShell comments for AD FS 2.0 to ensure that the caller is authorized on behalf of the subject to the relying party.

Enabling and checking Debug event log under AD FS 2.0 Tracing hasn't helped much. After spending about two days dedicated to this issue I gave up. We created a case at MS support. They collected data and proposed solution. There were two options: recreate AD FS farm or use unsupported script for changing ADFS service account (Active Directory Federation Services 2.x (AD FS 2.x): Change the Service Account). That was a real gem :) You can find a lot of information about internal AD FS architecture. After playing a bit with it I've found the root of the issue – SID of the initial AD FS service account was hardcoded in the AdfsConfiguration database.
Query for fix:

USE AdfsConfiguration

UPDATE IdentityServerPolicy.ServiceSettings
SET ServiceSettingsData=REPLACE((SELECT ServiceSettingsData from IdentityServerPolicy.ServiceSettings),'<old-sid>','<new-sid>')

I hope it will save some time for someone who will face that issue.