Configure Server 2016 ADFS and WAP with custom ports using Powershell

A pull request for Chris Gardner’s WebApplicationProxyDSC is now inbound after a frustrating week of trying to automate the configuration of ADFS and WAP on a Server 2016 lab.

With Server 2016, the PowerShell commands to configure the ADFS and WAP servers include switches to specify a non-default port. I need to do this because the servers are behind a NetNat on a server hosting several labs, so port 443 is not available to me and I must use a different port.

This should be simple: Specify the SSLPort switch on the Install-ADFSFarm command and the HttpsPort on the Install-WebApplicationProxy command. However, when I do that, the WAP configuration fails with an error that it cannot read the FederationMetadata from the proxy.

I tried all manner of things to diagnose why this was failing and in the end, the fix is a crazy hack that should not work!

The proxy installation, despite accepting the custom port parameter, does not build the URLs correctly for the ADFS service, so is still trying to call port 443. You can set these URLs on a configured WAP service using the Set-WebApplicationProxyConfiguration command. However, when you run this command with no configured proxy, it fails.

Or so you think…

On the ADFS Server:

  1. Install-AdfsFarm specifiying the CertificateThumbprint, Credential, FederationServiceDisplayName, FederationServiceName and SSLPort params

On the WAP Server:

  1. Install-WebApplicationProxy specifiying the HttpsPort switch,  CertificateThumbprint, FederationServiceName and FederationServiceTrustCredential params.
  2. Set-WebApplicationProxyConfiguration specifying the ADFSUrl, OAuthAuthenticationURL and ADFSSignOutURL parameters with the correct URLs for your ADFS server (which include the port in the Url).
  3. Re-run the command in step 1.

Despite the fact that step 2 says it failed, it seems to set enough information for step 3 to succeed. My experience, however, is that only doing steps 2 and 3 does not work. Weird!

As a side note, testing this lot is a lot easier if you remember that the idpinitiatedsignon.aspx page we all normally use for testing ADFS is disabled by default in Server 2016. Turn it on with Set-AdfsProperties -EnableIdPInitiatedSignonPage $true

Setting Enroll Permissions on ADCS Certificate Template using DSC

As part of the work I have been doing around generating and managing lab environments using Lability and DSC, one of the things I needed to do was change the permissions on a certificate template within a DSC configuration. Previously, when deploying to Azure, I used the PSPKI PowerShell modules within code executed by the Custom Script extension. I was very focused on sticking with DSC this time, which ruled out PSPKI. Whilst there is a DSC module available to configure Certificate Services itself, this does not extend to managing Certificate Templates.

Nobody seemed to have done exactly this before. I used the following links as references in creating the code:

Get Effective template permissions with PowerShell by Vadims Podans

Duplicate AD Object Without Active Directory PS Tools

Add Object Specific ACEs using Active Directory PowerShell

Using Scripts to Manage Active Directory Security

The script finds the WebServer template and grants the Enroll extended permission to the Domain Computers AD group. This allows me to use xCertificate in the DSC configuration of domain member servers to request new certificates using the WebServer template.

Here is the code I include in my DSC configuration. $DomainCreds is a PSCredential object for the domain admin ( I create the AD domain in an earlier step using xActiveDirectory).

        #Enable Enroll on WebServer certificate template         Script EnableWebServerEnroll {             DependsOn = "[xAdcsCertificationAuthority]CertAuth"             PsDscRunAsCredential = $DomainCreds             GetScript = {                 return @{ 'Result' = $true}             }             TestScript = {                 #Find the webserver template in AD and grant the Enroll extended right to the Domain Computers                 $filter = "(cn=WebServer)"                 $ConfigContext = ([ADSI]"LDAP://RootDSE").configurationNamingContext                 $ConfigContext = "CN=Certificate Templates,CN=Public Key Services,CN=Services,$ConfigContext"                  $ds = New-object System.DirectoryServices.DirectorySearcher([ADSI]"LDAP://$ConfigContext",$filter)                 $Template = $ds.Findone().GetDirectoryEntry()                 if ($Template -ne $null) {                     $objUser = New-Object System.Security.Principal.NTAccount("Domain Computers")                     # The following object specific ACE is to grant Enroll                     $objectGuid = New-Object Guid 0e10c968-78fb-11d2-90d4-00c04f79dc55                      ForEach ($AccessRule in $Template.ObjectSecurity.Access) {                         If ($AccessRule.ObjectType.ToString() -eq $objectGuid) {                             If ($AccessRule.IdentityReference -like "*$($objUser.Value)") {                                 Write-Verbose "TestScript: WebServer Template Enroll permission for Domain Computers exists. Returning True"                                 return $true                             }                         }                     }                 }                 return $false             }             SetScript = {                 #Find the webserver template in AD and grant the Enroll extended right to the Domain Computers                 $filter = "(cn=WebServer)"                 $ConfigContext = ([ADSI]"LDAP://RootDSE").configurationNamingContext                 $ConfigContext = "CN=Certificate Templates,CN=Public Key Services,CN=Services,$ConfigContext"                  $ds = New-object System.DirectoryServices.DirectorySearcher([ADSI]"LDAP://$ConfigContext",$filter)                 $Template = $ds.Findone().GetDirectoryEntry()                  if ($Template -ne $null) {                     $objUser = New-Object System.Security.Principal.NTAccount("Domain Computers")                     # The following object specific ACE is to grant Enroll                     $objectGuid = New-Object Guid 0e10c968-78fb-11d2-90d4-00c04f79dc55                     $ADRight = [System.DirectoryServices.ActiveDirectoryRights]"ExtendedRight"                     $ACEType = [System.Security.AccessControl.AccessControlType]"Allow"                     $ACE = New-Object System.DirectoryServices.ActiveDirectoryAccessRule -ArgumentList $objUser,$ADRight,$ACEType,$objectGuid                     $Template.ObjectSecurity.AddAccessRule($ACE)                     $Template.commitchanges()                     Write-Verbose "SetScript: Completed WebServer additional permission"                 }             }         }