This is a guide I have put together to hopefully save others the amount of time I have put into getting this working after two iterations. The goal is rather simple, which is to get a highly available ADFS environment online with Server 2016. There is quite a lot of documentation already out there, but certain aspects of the design choices in this implementation were not well covered so this is to help bridge those gaps. I’ve converted much of this process to Powershell, so that it will work on Server 2016 core/nano, but it’s not 100% there. I plan to finish those pieces soon and provide an update when I have those finished.
Prerequisites
- Active Directory: At least one domain controller running on Server 2012 or higher. Ideally all DC’s should be 2012 or higher.
- ADFS: Minimum of 4 servers running Server 2016 ( 2 node ADFS Farm + 2 WAP nodes)
- Domain Name: Decide on subdomain for ADFS. In the examples I will be using fs.jameysteinmann.com
- SSL Certificate: An ssl certificate valid for the domain you will be using for ADFS as well as the certauth subdomain of that. for this example we’re using fs.jameysteinmann.com and certauth.fs.jameysteinmann.com. If you’re not using workplace-join, the certauth subdomain is not needed
- Load Balancers: one to balance requests between the nodes in the adfs farm and another to balance requests to the WAP servers
- DNS
- Split DNS zone if ADFS is being exposed externally. The external DNS zone entries will point to the Load Balancer responsible for the WAP Servers and the internal DNS zone entries will point to the Load Balancer responsible for the ADFS Farm nodes.
- DNS entries for your ADFS subdomain (fs.jameysteinmann.com as used in this article) and one for WorkPlace-Join with is certauth added to the subdomain you choose (certauth.fs.jameysteinmann.com)
Design
- ADFS Farm: 2 Servers running Server 2016 utilizing the Windows Internal Database and joined to the domain
- WAPs: 2 Servers that can either be left in a workgroup or joined to the domain, but if they are joined, just please be aware of the implications.
- Group Managed Service Account for ADFS (This will be created when the first node of the ADFS farm is created)
Preparing For Setup
- Make sure all servers are up to date with the latest windows updates
- Install your SSL certificate on all ADFS and WAP servers
- Ensure, at a minimum, that the ADFS servers are joined to the domain
- Prepare AD for using gMSA
1
2
3#Check if a KDS Root Key exists. If one does, then no need to run the step after this
Get-KDSRootKey
Add-KDSRootKey -EffectiveImmediatelyIf you had to add a KDS Root Key, then it may take up to 10 hours in order to go into effect. If it is not replicated and ready, then creating any gMSA accounts will fail until it has completed.
- For DNS you’ll want to provision subdomains for the ADFS url you’re choosing. In the walkthrough below we’re using fs.jameysteinmann.com so you’ll want DNS records for both js.jameysteinmann.com and certauth.fs.jameysteinmann.com. If you’re not planning on using workplace-join then you can skip the certauth entry and it will just generate a warning when configuring ADFS.
Installing ADFS
Install The ADFS Feature
Run the following on each server that will act as an ADFS server.
1 | Install-WindowsFeature adfs-federation –IncludeManagementTools |
Configure The First ADFS Node
With the SSL certificate installed on all ADFS servers, retrieve the thumbprint of the certificate
1 | Get-ChildItem –path Cert:\LocalMachine\My\ | Where-Object { $_.Subject -like "*jameysteinmann.com*" }| fl |
This step will create the first node in the farm. The -OverwriteConfiguration is not necessary if this is done in a fresh environment, but I like to include it nonetheless. Since we’re using -GroupServiceAccountIdentifier, it will create the specified gMSA account in the Managed Service Accounts OU of the Active Directory Server and use it for running the service. You can replace the JAD\ADFSgmsa$ with anything you’d like so long as it has the $ attached at the end and references your AD domain name.
1 | Install-AdfsFarm -GroupServiceAccountIdentifier JAD\ADFSgmsa$ -CertificateThumbprint B3CACC95FAF0D72111E092B53E890EC1C212BA47 –OverwriteConfiguration -FederationServiceName "fs.jameysteinmann.com" -FederationServiceDisplayName "My Organization" |
After the node has been successfully created, we will move on to changing the default configuration before moving on to adding the other nodes. This is to avoid having to restart the ADFS services on each node once the changes have been made.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | #Add user agents to support Chrome/Firefox. Set-AdfsProperties -WIASupportedUserAgents @("MSAuthHost/1.0/In-Domain","MSIE 6.0","MSIE 7.0","MSIE 8.0","MSIE 9.0","MSIE 10.0","Trident/7.0","MSIPC","Windows Rights Management Client","MS_WorkFoldersClient","=~Windows\s*NT.*Edge","Mozilla/5.0") #Set Privacy Policy and Main website links in the ADFS login portal Set-AdfsGlobalWebContent -PrivacyLink https://www.jameysteinmann.com/privacy-policy/ -PrivacyLinkText 'Privacy Policy' Set-AdfsGlobalWebContent -HomeLink https://www.jameysteinmann.com -HomeLinkText Home #Set the logo to be used. Set-AdfsWebTheme -TargetName default -Logo @{path="C:\logos\My Logo 350x260.png"} #These two lines allow password changes to be done via the ADFS, this is not for Password Resets as that is unsupported Enable-AdfsEndpoint "/adfs/portal/updatepassword/" Set-AdfsEndpoint "/adfs/portal/updatepassword/" -Proxy:$true #Enable WS-Trust 1.3 which is supported in Office 2013/2016/365 Enable-AdfsEndpoint -TargetAddressPath "/adfs/services/trust/13/windowstransport" #Set ADFS lockouth threshold to match or be slightly more restrictive than AD lockout policy to prevent the AD accounts from being locked out via ADFS Set-AdfsProperties -EnableExtranetLockout:$true -ExtranetLockoutThreshold 5 -ExtranetObservationWindow (New-TimeSpan -Minutes 30) -ExtranetLockoutRequirePDC $false Restart-Service AdfsSrv –Force |
Fixing the gMSA
This was a huge pain point for me in the ADFS setup. For whatever reason, the first node you create is happy with how the gMSA account is setup, but it can cause adding any other nodes to fail since it doesn’t seem to add the correct permissions for accessing the gMSA’s managed password. I received many errors depending on if you did the setup of the second node via GUI or Powershell. The former ran through all prerequisite test cases and generated many errors, while powershell only ran one and failed there. None of the errors about “username or password incorrect” or “There were no SPNs set on the following service account…” pointed me in the right direction. If this is the case for you, here is how we validate the issue.
First let’s verify all the required SPNs were created when we installed the first node.
1 2 3 4 | PS C:\Windows\system32> setspn -L ADFSgmsa Registered ServicePrincipalNames for CN=ADFSgmsa,CN=Managed Service Accounts,DC=local,DC=jameysteinmann,DC=com: HTTP/fs.jameysteinmann.com host/fs.jameysteinmann.com |
Next we want to make sure The ADFS servers and the user account you’re using to install the subsequent nodes have permission to use the gMSA account and retrieve it’s password. This was the cause of the dreaded issues above in that the user account I was using to install the additional nodes was not allowed to retrieve the password.
Let’s see who is authorized currently:
1 2 3 4 | PS C:\Windows\system32> Get-ADServiceAccount ADFSgMSA -Properties * | Select-Object Name,PrincipalsAllowedToRetrieveManagedPassword|fl Name : ADFSAdmin PrincipalsAllowedToRetrieveManagedPassword : {CN=ADFS01,OU=Servers,DC=local,DC=jameysteinmann,DC=com} |
Now let’s add the missing accounts
1 2 3 4 5 6 7 8 9 10 11 12 13 | #get existing Principals $adfsgmsa = Get-ADServiceAccount ADFSgMSA -Properties PrincipalsAllowedToRetrieveManagedPassword #get DNs for other principals we're adding to the gMSA $principals = @( ((Get-ADUser MyAdminUser).DistinguishedName), ((Get-ADComputer ADFS02).DistinguishedName) ) #add new the two arrays $principals+=$adfsgmsa.PrincipalsAllowedToRetrieveManagedPassword #set the ad service account to use all principals Set-ADServiceAccount -Identity 'adfsgmsa' -PrincipalsAllowedToRetrieveManagedPassword $principals #verify the changes (this might take a while to go into effect) Get-ADServiceAccount ADFSgMSA -Properties PrincipalsAllowedToRetrieveManagedPassword |
This may take some time to replicate, but once it’s in place we should have no issues adding the second node.
Configure the Second ADFS Node
Before we configure the second node on the farm, let’s run one last test to verify everything should go smoothly
1 2 3 4 5 6 7 8 9 | Test-AdfsFarmInstallation -GroupServiceAccountIdentifier JAD\adfsgmsa$ -FederationServiceName fs.jameysteinmann.com Message Context Status ------- ------- ------ Successfully verified the target computer is joined to a domain. PreCheckTest Success Successfully validated the port settings. PrerequisiteTest Success Successfully verified the user is a member of Domain Admins of the target machine's domain. PrerequisiteTest Success KDS key requirements were validated. PrerequisiteTest Success group Managed Service Account was validated. PrerequisiteTest Success |
With all the tests passing, like installing the first node, adding the second node is a straight forward single command.
1 | Add-AdfsFarmNode -PrimaryComputerName "adfs01.local.jameysteinmann.com" -GroupServiceAccountIdentifier JAD\ADFSgMSA$ -CertificateThumbprint B3CACC95FAF0D72111E092B53E890EC1C212BA47 -OverwriteConfiguration |
At this point we have a 2 node ADFS farm up and running. We can test the sign in functionality via the IdpInitiatedSignon page. This is disabled by default but is rather simply turned on.
1 2 | Set-AdfsProperties -EnableIdPInitiatedSignonPage $true Get-AdfsProperties | Select-Object EnableIdpInitiatedSignonpage |
Once it is enabled, visit your ADFS IdpInitiatedSignonpage: https://fs.jameysteinmann.com/adfs/ls/idpinitiatedsignon.aspx and you should be able to sign in and out.
Wrapping up with WAPs
For the ADFS Proxy servers, two steps are all that’s needed since the SSL certificate should already be installed on the server.
1 2 3 4 | #Install the WAP feature Install-WindowsFeature Web-Application-Proxy #Configure the WAP for the ADFS Farm Install-WebApplicationProxy -CertificateThumbprint 'B3CACC95FAF0D72111E092B53E890EC1C212BA47' -FederationServiceName fs.jameysteinmann.com |
That’s it. At this point you should be able to access the ADFS internally via the Load Balancer for the ADFS farm and externally via the Load Balancer for the WAPs.