Jorge's Quest For Knowledge!

All About Identity And Security On-Premises And In The Cloud – It's Just Like An Addiction, The More You Have, The More You Want To Have!

(2016-12-16) Automatic Azure AD Join With ADFS v3.0 And Higher And Conditional Access – What You Really Need In Detail

Posted by Jorge on 2016-12-16


With regards to auto Azure AD joining computers to Azure you can read the following documentation:

…and if you also want to go all crazy about all kinds of details, you must definitely read Jairo’s blog posts about Azure AD join and related matters. These are:

So WHY should you read this (my) blog post?

If you keep to the defaults that Microsoft provides in the documentation above, you are good to go!. However, and here comes the answer to the “WHY?”, if you want or need to deviate from the default you might not be able to get auto Azure AD joining working!

So, let’s start by going at it piece by piece!

Synchronize Computer Accounts Azure AD Join Or Auto Azure AD Join:

With Azure AD multiple join definitions exist. The definition I’m focusing on this blog is joining your Windows 7/8.1/10 PCs, that already joined to the on-premises AD, to Azure AD. There are two ways to achieve that goal, although with some conditions.

With Windows 7/8.1/10, if you want to have your AD domain joined PCs automatically and instantly joined to Azure AD, then you should continue to read remainder of this blog.With Windows 7/8.1/10 the computer is automatically register in Azure AD as soon as it starts and all conditions are met or in place.

With Windows 7/8.1 registration is done at user logon or when unlocking the PC and is done under the user context. The registration is done as user @ device. Therefore in Azure AD you may find multiple device registrations for the same Windows 7/8.1 PC and the reason for that is that multiple users are using the same PC. Remember that the scheduled tasks that triggers the registration has or may have a delay configured. So if you log on and nothing happens immediately, then think about the delay configured in the scheduled task. Windows 7 @ user logon delay is 5 min., Windows 7 @ workstation unlock there is no delay, Windows 8.1 @ user logon delay is 5 min.. Something else to be aware is that a non-admin user can unregister and register his own Windows 7/8.1 PC. Every a registration occurs for a specific PC initially or after an unregistration a new device object is created. Therefore, it might see may device objects in Azure AD for Windows 7/8.1 PCs and that is caused by multiple users using the PC or 1 single user that has reregistered his PC multiple times

With Windows 10 registration is done at user logon and is done under the computer context. The registration is really done as a device. For every Windows 10 PC you should only find one corresponding device object. Remember that the scheduled tasks that triggers the registration has or may have a delay configured. So if you log on and nothing happens immediately, then think about the delay configured in the scheduled task. Windows 10 @ user logon delay is 1 min.. Oh, and if a device object already exists that corresponds to the registering PC, because it was created by Azure AD Connect, then that device object is used. Compared to Windows 7/8.1, in Windows 10 non-admin users cannot unregister and register the WIndows 10 PC as it is the PC itself that must do this.

The part above is about automatically and instantly registering devices in Azure AD, or in other words provisioning. How about deprovisioning? When you unregister a device in Windows 7/8.1 the user @ device object is not cleaned automatically. You must have an out-of-band process to clean those. When you unregister a device in Windows 10 the device object is cleaned automatically and there is no need clean it up yourself. So what happens then if you just throw away the computer account in AD? Well, with Windows 7/8.1 nothing will happen in Azure AD. Again you will have to clean that yourself. With Windows 10 however, it depends on your Azure AD Connect configuration, so let’s talk about that!

With Azure AD Connect, you can select the OUs containing your Windows 10 PCs and bring those into the scope of synchronization. When you do that you achieve two goals. The first goal is that you will be able to provision device objects in Azure AD for those PCs that have not registered (yet) against Azure AD automatically. So if you do not use auto Azure AD joining for Windows 10 PCs, then Azure AD Connect will do it for you, although in a delayed manner due to the synchronization interval of Azure AD Connect. If a PC has auto registered itself, then Azure AD Connect will match the device object to the computer account in AD automatically next time Azure AD Connect runs. In either case, with or without auto Azure AD joining, Azure AD Connect will delete the device object as soon as the computer account has been deleted in AD or has been removed from the synchronization scope of Azure AD. Management of device objects in Azure AD (provisioning and deprovisioning) by Azure AD Connect is only possible for Windows 10 PCs as those are really registered as device objects representing the PC. It is does not support Windows 7/8.1 PCs are those are registered as device objects representing the user on the PC.

Therefore:

  • 1 Windows 7/8.1 PC can have multiple device objects in Azure AD. How many device objects, depends on the number of users using that Windows 7/8.1PC
  • 1 Windows 10 PC will have 1 device object in Azure AD, regardless the amount of users using that Windows 10 PC

Workplace Join Software And Triggering Registration:

Windows 8.1 and 10 do not require any additional software. All the required software is already integrated in Windows. Windows 7, however, does not have the required software. The required software must be installed manually or through some distribution system. Be aware that there are 2 versions of this software!

One version is available through Microsoft Connect. You can access it through this link. The other most recent version is available through Microsoft Download. You can access it through this link. One noticeable difference between the 2 versions is that the Microsoft Connect version does not use the SCP, even if it is in place. This version will always use the suffix of the UPN to determine the FQDN of the corresponding federated domain in Azure AD. If your UPN is non-internet routable, then you have a problem. The Microsoft Download version will also always use the suffix of the UPN to determine the FQDN of the corresponding federated domain in Azure AD, if the SCP is not in place in AD. If the SCP is in place, then it will use that information instead. If your UPN is non-internet routable, then this will work for you!

My suggestion for you is to use the Microsoft Download version!

Windows 7 will register as soon as all conditions are met. The same applies for Windows 8.1 and 10, but in addition to trigger the registration you must have a GPO with the correct setting enabled targeting the Windows 8.1/10 PCs in AD.

  • GPO Setting to trigger device registration in Azure AD for Windows 8.1/10:
    • From W2K12R12/Win81 machine –> Computer Configuration > Policies > Administrative Templates > Windows Components > Workplace Join > Automatically workplace join client computers = Enabled
    • From W2K16/Win10 machine –> Computer Configuration/Policies/Administrative Templates/Windows Components/Device Registration > Automatically workplace join client computers = Enabled

DNS Records For Device Registration:

If an operating system does not use the SCP, but rather uses the suffix of the UPN, then you need to create the correct DNS records for every suffix that you must support. If an operating system does use the SCP, then you need to create the correct DNS records for every FQDN that is listed in any SCP. You can only have one SCP for Azure AD per AD. If you have multiple ADs, you may also need an SCP for every AD. More information below. Check the documentation for the exact information about the required DNS records.

I have found out that this also works WITHOUT the “enterprise.<your federated domain FQDN>” DNS CNAME record when using the SCP. I do not know what would happen if it uses the suffix of the UPN. I would assume the same behavior

Service Connection Point (SCP):

For your Windows clients (Windows 7, 8.1, 10 and probably higher) to be able to register against your Azure AD tenant, those clients need to find the  registration service endpoint. For that to succeed, you need to create a SCP through a PowerShell CMDlet in Azure AD Connect. Check the documentation for the exact syntax of the command and the information about the object in AD. After running the command, the “keywords” attribute contains the Azure AD Tenant ID and the FQDN of one of the federated domains if you have any. If you have multiple federated domains, the command will just choose one. It does not really matter which one, at least if all federated domains in Azure AD point to the same on-premises federation system. If you have multiple federated domains and those point to different on-premises federation systems, then you need to tweak the SCP manually by specifying the correct FQDN. Remember that any given AD forest can only have one SCP with Azure AD Tenant information. If you have multiple AD forests being served by the same federation system, then you need to create an SCP in every AD forest where you want to support auto Azure AD join. For the latter scenario, you can perform an LDIFDE export of the object where it already exists, adjust the information as needed and perform an LDIFDE import in the other AD forest. Remember that for every FQDN that you use, you must have the correct DNS records in place as mentioned earlier!

As LDIFDE is sooooooooooooooo before 2005, you can also use PowerShell to create the SCP in other AD forest. The only thing you need to know is the FQDN of a federated domain in AAD and the AAD tenant ID. Knowing that you can use the following script:

$verifiedDomain = "<Replace this with the FQDN of any of your verified domain names in Azure AD>"
$tenantID = "<Replace this with you tenant ID>"

$thisADForest = [DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest()
$configNC = $($thisADForest.schema.name).Substring($($thisADForest.schema.name).IndexOf(",") + 1)

$directoryEntry = New-Object System.DirectoryServices.DirectoryEntry
$directoryEntry.Path = "LDAP://CN=Services," + $configNC

$deviceRegContainer = $directoryEntry.Children.Add("CN=Device Registration Configuration", "container")
$deviceRegContainer.CommitChanges()

$serviceConnectionPoint = $deviceRegContainer.Children.Add("CN=62a0ff2e-97b9-4513-943f-0d221bd30080", "serviceConnectionPoint")
$serviceConnectionPoint.Properties["keywords"].Add("azureADName:" + $verifiedDomain)
$serviceConnectionPoint.Properties["keywords"].Add("azureADId:" + $tenantID)

$serviceConnectionPoint.CommitChanges()

image

Figure 1: SCP Object In AD

Authentication:

With auto Azure AD join you are joining PCs to Azure AD that are already joined to AD. Auto Azure AD join always occurs within the corporate network and leverages Windows authentication with no interaction. Therefore, within ADFS you must have “Windows Integrated Authentication (WIA)” as the first authentication mechanism. You CANNOT use anything like Forms Based Authentication (FBA) or Certificate Based Authentication (CBA).

Home Realm Discovery:

With auto Azure AD join you cannot have any interaction. With that in mind you must therefore sure the Home Realm Discovery (HRD) screen in ADFS does not interact. If you only have one claims provider/identity provider trust (i.e “Active Directory”), then you are good to go! However, if you have multiple claims provider/identity provider trusts, you must make sure that only the “Active Directory” CP trust is allowed to be used. Within ADFS v3.0 and higher, every RP trust has a property called “ClaimsProviderName” and you need to make sure that only “Active Directory” is listed in that property. When that is the case, ADFS will always assume that everyone needs to be authenticated by the local Active Directory or any other 2-way forest trust connected Active Directory.

image

Figure 2: RP Trust For Azure AD Configured For Only The CP Trust Active Directory

Authentication Class References:

By default the RP trust for Azure AD is not configured with any authentication class reference. For Windows 7/8.1/10 you must configure the RP trust for Azure AD to have “wiaormultiauthn” as an allowed authentication class reference. See the documentation for how to configure this.

image

Figure 3: Required Authentication Class Reference

image

Figure 4: Sending Authentication Class Reference In Claims

The yellow marked claim rule (rule 11):

@RuleName = "AuthN Claims – Methods References"
c:[Type == "
http://schemas.microsoft.com/claims/authnmethodsreferences"%5D
=> issue(claim = c);

If you do not configure this, you will see a error (event ID 364) in the ADFS logs exactly the same or similar to

Encountered error during federation passive request.

Additional Data

Protocol Name:
wsfed

Relying Party:
urn:federation:MicrosoftOnline

Exception details:
Microsoft.IdentityServer.RequestFailedException: The requested authentication method ‘wiaormultiauthn’ is not valid for relying party trust ‘Microsoft Azure Active Directory (Custom Config)’.
   at Microsoft.IdentityServer.Web.Authentication.AuthenticationPolicyEvaluator.ValidateRpAllowsAuthClassReference(String rpIdentifier, String authClassReferenceId)
   at Microsoft.IdentityServer.Web.Authentication.AuthenticationPolicyEvaluator.VerifyTargetRPsSupportAuthClassReference(String authClassReferenceId)
   at Microsoft.IdentityServer.Web.Authentication.AuthenticationPolicyEvaluator.EvaluatePolicy(Boolean& isLastStage, AuthenticationStage& currentStage, Boolean& strongAuthRequried)
   at Microsoft.IdentityServer.Web.PassiveProtocolListener.GetAuthMethodsFromAuthPolicyRules(PassiveProtocolHandler protocolHandler, ProtocolContext protocolContext)
   at Microsoft.IdentityServer.Web.PassiveProtocolListener.GetAuthenticationMethods(PassiveProtocolHandler protocolHandler, ProtocolContext protocolContext)
   at Microsoft.IdentityServer.Web.PassiveProtocolListener.OnGetContext(WrappedHttpListenerContext context)

ADFS Endpoints:

Windows 10 and Windows Server 2016 and higher will authenticate by using Windows Integrated Authentication to an active WS-Trust endpoint hosted by ADFS. It is therefore very important that this endpoint is enabled. Check the documentation for the correct endpoint and how to enable that endpoint if not already enabled. This is not needed for Windows 7/8.1.

However, ADFS provides 2 ADFS endpoint that support this requirement. By default, the endpoint “/adfs/services/trust/2005/windowstransport” is enabled by default for ADFS and the WAP (Proxy) and by default the endpoint “/adfs/services/trust/13/windowstransport” is disabled by default for ADFS and WAP. The documentation will tell you to enable “/adfs/services/trust/13/windowstransport”. However, if you already have “/adfs/services/trust/2005/windowstransport” enabled for ADFS you may not need to enable “/adfs/services/trust/13/windowstransport” for ADFS.

Some documentation, like Windows 10 Sign on – enabling device authentication with AD FS will tell you to enable both, but that is not really needed.

ClientCredentialType : Windows
Enabled              : True
FullUrl              :
https://<FQDN Federation Service Farm>/adfs/services/trust/2005/windowstransport
Proxy                : True
Protocol             : WS-Trust
SecurityMode         : Transport
AddressPath          : /adfs/services/trust/2005/windowstransport
Version              : wstrust2005

ClientCredentialType : Windows
Enabled              : False
FullUrl              :
https://<FQDN Federation Service Farm>/adfs/services/trust/13/windowstransport
Proxy                : False
Protocol             : WS-Trust
SecurityMode         : Transport
AddressPath          : /adfs/services/trust/13/windowstransport
Version              : wstrust13

If you do not have a WS-trust endpoint enabled, you will see a error (event ID 305) in the ADFS logs exactly the same or similar to:

Automatic registration failed at authentication phase.  Unable to acquire access token.  Exit code: Unspecified error. Server error: AdalMessage: ADALUseWindowsAuthenticationTenant failed,  unable to preform integrated auth
AdalError: authentication_failed
AdalErrorCode: 0x2ee6
AdalCorrelationId: {18691F25-1E05-4D4E-BD43-28BD34D879E9}
AdalLog:  HRESULT: 0x2ee6
AdalLog:  HRESULT: 0x2ee6
AdalLog:  HRESULT: 0x2ee6
AdalLog: AggregatedTokenRequest::UseWindowsIntegratedAuth- received realm info ; HRESULT: 0x0
AdalLog:  HRESULT: 0x4aa90010
AdalLog: AggregatedTokenRequest::UseWindowsIntegratedAuth w Tenant ; HRESULT: 0x0
AdalLog: AggregatedTokenRequest::AcquireToken- returns false ; HRESULT: 0x0
AdalLog: AggregatedTokenRequest::AcquireToken- refresh token is not available ; HRESULT: 0x0
AdalLog: AggregatedTokenRequest::AcquireToken get refresh token info ; HRESULT: 0x0
AdalLog: Authority validation is completed ; HRESULT: 0x0
AdalLog: Authority validation is enabled ; HRESULT: 0x0
AdalLog: Token is not available in the cache ; HRESULT: 0x0
. Tenant Type: XXXXX.XXX

If you do not have a WS-trust endpoint enabled, you will see a error (event ID 304) in the ADFS logs exactly the same or similar to:

Automatic registration failed at join phase.  Exit code: Unknown HResult Error code: 0xcaa1000e. Server error: empty. Debug Output:\r\n joinMode: Join
drsInstance: azure
registrationType: fed
tenantType: fed
tenantId: 550d46cf-0819-4a9b-a03c-2f0fd431ce1f
configLocation: undefined
errorPhase: auth
adalCorrelationId: {18691F25-1E05-4D4E-BD43-28BD34D879E9}
adalLog: AdalLog:  HRESULT: 0xcaa1000e
AdalLog:  HRESULT: 0x2ee6
AdalLog:  HRESULT: 0x2ee6
AdalLog:  HRESULT: 0x2ee6
AdalLog: AggregatedTokenRequest::UseWindowsIntegratedAuth- received realm info ; HRESULT: 0x0
AdalLog:  HRESULT: 0x4aa90010
AdalLog: AggregatedTokenRequest::UseWindowsIntegratedAuth w Tenant ; HRESULT: 0x0
AdalLog: AggregatedTokenRequest::AcquireToken- returns false ; HRESULT: 0x0
AdalLog: AggregatedTokenRequest::AcquireToken- refresh token is not available ; HRESULT: 0x0
AdalLog: AggregatedTokenRequest::AcquireToken get refresh token info ; HRESULT: 0x0
AdalLog: Authority validation is completed ; HRESULT: 0x0
AdalLog: Authority validation is enabled ; HRESULT: 0x0
AdalLog: Token is not available in the cache ; HRESULT: 0x0

adalLog: AdalLog:  HRESULT: 0xcaa1000e
AdalLog:  HRESULT: 0x2ee6
AdalLog:  HRESULT: 0x2ee6
AdalLog:  HRESULT: 0x2ee6
AdalLog: AggregatedTokenRequest::UseWindowsIntegratedAuth- received realm info ; HRESULT: 0x0
AdalLog:  HRESULT: 0x4aa90010
AdalLog: AggregatedTokenRequest::UseWindowsIntegratedAuth w Tenant ; HRESULT: 0x0
AdalLog: AggregatedTokenRequest::AcquireToken- returns false ; HRESULT: 0x0
AdalLog: AggregatedTokenRequest::AcquireToken- refresh token is not available ; HRESULT: 0x0
AdalLog: AggregatedTokenRequest::AcquireToken get refresh token info ; HRESULT: 0x0
AdalLog: Authority validation is completed ; HRESULT: 0x0
AdalLog: Authority validation is enabled ; HRESULT: 0x0
AdalLog: Token is not available in the cache ; HRESULT: 0x0

adalResponseCode: 0xcaa1000e

To enable the "/adfs/services/trust/13/windowstransport" endpoint for ADFS, execute on the (primary) ADFS server:

Enable-AdfsEndpoint -TargetAddressPath "/adfs/services/trust/13/windowstransport"

To enable the "/adfs/services/trust/2005/windowstransport" endpoint for ADFS, execute on the (primary) ADFS server:

Enable-AdfsEndpoint -TargetAddressPath "/adfs/services/trust/2005/windowstransport"

Claims Rules For The RP Trust For Azure AD (User Authentication/Access):

For a user to successfully authenticate against Azure AD you must configure a number of claims rules. The claims rules are listed below and I will explain them one by one. By the way, the claims rules you see are the claims rules I have for MY environment. Your claims rules should be either exactly the same of similar to what you see below. For this part of this blog post it is also important that you also read the following blog posts to have the complete picture:

Azure AD Connect – Identifying Objects In AD And In Azure AD

image

Figure 5: Claims Rules For User Authentication Against Azure AD On the Azure AD RP Trust

The yellow marked claim rule (rule 1):

@RuleName = "Identity Claims – upn To UPN"
c:[Type == "
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn"%5D
=> issue(Type = "http://schemas.xmlsoap.org/claims/UPN&quot;, Issuer = c.Issuer, OriginalIssuer = c.OriginalIssuer, Value = c.Value, ValueType = c.ValueType);

EXPLANATION: For the user to successfully authenticate against Azure AD, the value of the AD attribute that you chose during the installation of Azure AD Connect as the source for the User Principal Name in Azure AD must be send in a claim type to Azure AD. By default, that is the AD attribute “userPrincipalName”. You may have chosen something else, e.g. mail, if your AD attribute “userPrincipalName” contains values with non-internet routable suffixes. Also see (2016-05-07) Azure AD Connect – Identifying Objects In AD And In Azure AD (Part 1) .

The yellow marked claim rule (rule 2):

@RuleTemplate = "MapClaims"
@RuleName = "Identity Claims – iamTECImmutableID To ImmutableID"
c:[Type == "
http://temp.org/identity/claims/iamTECImmutableID"%5D
=> issue(Type = "http://schemas.microsoft.com/LiveID/Federation/2008/05/ImmutableID&quot;, Issuer = c.Issuer, OriginalIssuer = c.OriginalIssuer, Value = c.Value, ValueType = c.ValueType);

EXPLANATION: For the user to successfully authenticate against Azure AD, an additional immutable identifier must be send to Azure AD in a claim type. Now why is this, you may think, if the previous claim rule already sends identification to Azure AD. See this claim type as an addition security measure that really links the user in Azure AD to the user in your AD, and not to somebody else’s AD that is using the same UPN suffix (e.g. hacker). This immutable ID is a unique value that nobody can guess easily. The value of the AD attribute that you chose for users during the installation of Azure AD Connect as the source for the Source Anchor in Azure AD must be send in a claim type to Azure AD. By default, that is the AD attribute “objectGUID”. If you have read the blog post (2016-05-07) Azure AD Connect – Identifying Objects In AD And In Azure AD (Part 1) and the reasons mentioned there apply to you (even if they do not, I really suggest you follow that guidance to give yourself room in the future if you need it!), you have chosen another AD attribute to be the source of the Source Anchor. As you can see above I’m using a special claim type “http://temp.org/identity/claims/iamTECImmutableID” that contains my immutable ID value for Azure AD. That value is put into the claim type “http://schemas.microsoft.com/LiveID/Federation/2008/05/ImmutableID” that Azure AD wants to receive. I will explain below how I get my value for “http://temp.org/identity/claims/iamTECImmutableID

The yellow marked claim rule (rule 3):

@RuleTemplate = "MapClaims"
@RuleName = "Identity Claims – iamTECImmutableID To NameID"
c:[Type == "
http://temp.org/identity/claims/iamTECImmutableID"%5D
=> issue(Type = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier&quot;, Issuer = c.Issuer, OriginalIssuer = c.OriginalIssuer, Value = c.Value, ValueType = c.ValueType, Properties["http://schemas.xmlsoap.org/ws/2005/05/identity/claimproperties/format"%5D = "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified");

EXPLANATION: The exact value you send as the immutable ID to “http://schemas.microsoft.com/LiveID/Federation/2008/05/ImmutableID” must also be send to “http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier” and in addition using the format specified in the claim rule for the name identifier. Again, the value of the AD attribute that you chose for users during the installation of Azure AD Connect as the source for the Source Anchor in Azure AD must be send in a claim type to Azure AD. By default, that is the AD attribute “objectGUID”. If you have read the blog post (2016-05-07) Azure AD Connect – Identifying Objects In AD And In Azure AD (Part 1) and the reasons mentioned there apply to you (even if they do not, I really suggest you follow that guidance to give yourself room in the future if you need it!), you have chosen another AD attribute to be the source of the Source Anchor. As you can see above I’m using a special claim type “http://temp.org/identity/claims/iamTECImmutableID” that contains my immutable ID value for Azure AD. That value is put into the claim type “http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier” that Azure AD wants to receive. I will explain below how I get my value for “http://temp.org/identity/claims/iamTECImmutableID”.

The yellow marked claim rule (rule 4):

@RuleName = "Identity Claims – Issuer Identifier"
c1:[Type == "
http://schemas.xmlsoap.org/claims/UPN&quot;, Value =~ "(?i)@(xxxxxx|yyyyyy).nl$"]
&& c2:[Type == "
http://temp.org/identity/claims/primaryGroupID&quot;, Value =~ "^513$"]
=> issue(Type = "
http://schemas.microsoft.com/ws/2008/06/identity/claims/issuerid&quot;, Value = regexreplace(c1.Value, ".+@(?<domain>.+)", "<Federation Service Identifier>:AAD:${domain}"));

EXPLANATION: This claims rule is NOT required if you only have 1 federated domain in Azure AD. If you have multiple federated domains in Azure AD, then a claims rule similar to this one is required. That claims rule is configured automatically when using the “SupportMultipleDomain” option as explained here. The reason for this is that every federated domain in Azure AD must have a unique identifier. If multiple federated domains point to the same federation system on-premises, the identifier will be the same across multiple federated domains, and Azure AD does not allow that. That’s why, if you have multiple federated domains, you have to send an additional claim type that contains a custom unique issuer ID.

I my case I only to apply this to users, hence the use of the “http://temp.org/identity/claims/primaryGroupID” claim type with value “513” (I will explain below how I get this value). This basically says the following:

If a UPN value is present that ends in “@xxxxxx.nl” or “@yyyyyy.nl” and a primaryGroupID value of “513” exist, then issue the claim type “http://schemas.microsoft.com/ws/2008/06/identity/claims/issuerid” with a value of “<Federation Service Identifier>:AAD:#######” where “#######” is replaced by either “xxxxxx.nl” or “yyyyyy.nl”. The value for <Federation Service Identifier> is in my case exactly the same as the identifier of my ADFS farm. For this to work, the value “<Federation Service Identifier>:AAD:xxxxxx.nl” must exist as an issueURI on the domain “XXXXXX.NL” in Azure AD and the value “<Federation Service Identifier>:AAD:yyyyyy.nl” must exist as an issueURI on the domain “YYYYYY.NL” in Azure AD.

In the Microsoft documentation, or after running the PowerShell CMDlet with the “SupportMultipleDomain” option you will not find the value "<Federation Service Identifier>:AAD:${domain}", but rather you will find “http://${domain}/adfs/services/trust/”. Whatever value you use, it does not really matter. The only rule that applies is: whatever you send in the issuer ID claim type, it must match the issuer =URI of the targeted federated domain in Azure AD. If you need to support sub-domains of federated domains, than also look here.

Claims Rules For The RP Trust For Azure AD (Computer Authentication/Access):

For a Windows 10 computer to successfully authenticate against Azure AD you must configure a number of claims rules. The claims rules are listed below and I will explain them one by one. By the way, the claims rules you see are the claims rules I have for MY environment. Your claims rules should be either exactly the same of similar to what you see below.

image

Figure 6: Claims Rules For Computer Authentication Against Azure AD On the Azure AD RP Trust

The yellow marked claim rule (rule 5):

@RuleName = "Machine Claims – ObjectGUID To ImmutableID"
c1:[Type == "
http://temp.org/identity/claims/primaryGroupID&quot;, Value =~ "^515$"]
&& c2:[Type == "
http://temp.org/identity/claims/adObjectGuidBase64org"%5D
=> issue(Type = "http://schemas.microsoft.com/LiveID/Federation/2008/05/ImmutableID&quot;, Issuer = c2.Issuer, OriginalIssuer = c2.OriginalIssuer, Value = c2.Value, ValueType = c2.ValueType);

EXPLANATION: For the computer to successfully authenticate against Azure AD, an additional immutable identifier must be send to Azure AD in a claim type. The value of the AD attribute that you chose for users during the installation of Azure AD Connect as the source for the Source Anchor in Azure AD is NOT relevant for computers. Although it is possible to change this, for computers by default, the value of the AD attribute “objectGUID” is used as the immutable ID to identify the computer. As you can see above I’m using a special claim type “http://temp.org/identity/claims/adObjectGuidBase64org” that contains my immutable ID value for Azure AD. That value is put into the claim type “http://schemas.microsoft.com/LiveID/Federation/2008/05/ImmutableID” that Azure AD wants to receive. I will explain below how I get my value for “http://temp.org/identity/claims/adObjectGuidBase64org”. As you have seen before I’m now explicitly filtering for primaryGroupID being equal to 515 and that means that it will only apply for computers. I will explain below how I get the value for “http://temp.org/identity/claims/adObjectGuidBase64org” and “http://temp.org/identity/claims/primaryGroupID”.

The yellow marked claim rule (rule 6):

@RuleName = "Machine Claims – ObjectGUID To OnPremObjectGUID"
c1:[Type == "
http://temp.org/identity/claims/primaryGroupID&quot;, Value =~ "^515$"]
&& c2:[Type == "
http://temp.org/identity/claims/adObjectGuidBase64org"%5D
=> issue(Type = "http://schemas.microsoft.com/identity/claims/onpremobjectguid&quot;, Value = c2.Value);

EXPLANATION: The exact value you send as the immutable ID to “http://schemas.microsoft.com/LiveID/Federation/2008/05/ImmutableID” must also be send to “http://schemas.microsoft.com/identity/claims/onpremobjectguid”. Again, the value of the AD attribute that you chose for users during the installation of Azure AD Connect as the source for the Source Anchor in Azure AD is NOT relevant for computers. Although it is possible to change this, for computers by default, the value of the AD attribute “objectGUID” is used as the immutable ID to identify the computer. As you can see above I’m using a special claim type “http://temp.org/identity/claims/adObjectGuidBase64org” that contains the immutable ID value for computers in Azure AD. That value is put into the claim type “http://schemas.microsoft.com/identity/claims/onpremobjectguid” that Azure AD wants to receive. I will explain below how I get my value for “http://temp.org/identity/claims/adObjectGuidBase64org”. As you have seen before I’m now explicitly filtering for primaryGroupID being equal to 515 and that means that it will only apply for computers. I will explain below how I get the value for “http://temp.org/identity/claims/adObjectGuidBase64org” and “http://temp.org/identity/claims/primaryGroupID”.

The yellow marked claim rule (rule 7):

@RuleName = "Machine Claims – PrimarySid"
c1:[Type == "http://temp.org/identity/claims/primaryGroupID&quot;, Value =~ "^515$"]
&& c2:[Type == "http://schemas.microsoft.com/ws/2008/06/identity/claims/primarysid"%5D
=> issue(claim = c2);

EXPLANATION: For computers only, hence the additional filter/condition using the “http://temp.org/identity/claims/primaryGroupID” claim type containing the value “515”, the objectSID must be send to Azure AD. Therefore if a claim type “http://schemas.microsoft.com/ws/2008/06/identity/claims/primarysid” exist for computers with a value, then that claim type and value must be send to Azure AD. I will explain below how I get the value for “http://temp.org/identity/claims/primaryGroupID”.

The yellow marked claim rule (rule 8):

@RuleName = "Machine Claims – Account Type"
c:[Type == "
http://temp.org/identity/claims/primaryGroupID&quot;, Value =~ "^515$"]
=> issue(Type = "
http://schemas.microsoft.com/ws/2012/01/accounttype&quot;, Value = "DJ");

EXPLANATION: For computers only, hence the additional filter/condition using the “http://temp.org/identity/claims/primaryGroupID” claim type containing the value “515”, a special claim type “http://schemas.microsoft.com/ws/2012/01/accounttype” with a value of DJ must be send to Azure AD. This is telling Azure AD that the computer in question is already domain joined in AD. I will explain below how I get the value for “http://temp.org/identity/claims/primaryGroupID”.

The yellow marked claim rule (rule 9):

@RuleName = "Machine Claims – Issuer Identifier"
c:[Type == "
http://temp.org/identity/claims/primaryGroupID&quot;, Value =~ "^515$"]
=> issue(Type = "
http://schemas.microsoft.com/ws/2008/06/identity/claims/issuerid&quot;, Value = "<Federation Service Identifier>:AAD:XXXXXX.NL");

EXPLANATION: This claims rule is NOT required if you only have 1 federated domain in Azure AD. If you have multiple federated domains in Azure AD, then a claims rule similar to this one is required. As for users, when having multiple federated domains in Azure AD, for computers you must also send the “http://schemas.microsoft.com/ws/2008/06/identity/claims/issuerid” claim type with a specific value. However for computers only, hence the additional filter/condition using the “http://temp.org/identity/claims/primaryGroupID” claim type containing the value “515”, the value does not matter. As long as the value matches the issuerURI of one of the federated domains in Azure AD, then that’s OK. Which one to choose? Does not matter, just pick your favorite! I will explain below how I get the value for “http://temp.org/identity/claims/primaryGroupID”.

Claims Rules For The CP Trust For AD:

For a user and a computer to successfully authenticate against ADFS and have the required claim types and values for Azure AD you must configure a number of claims rules. The claims rules are listed below as the bare minimum to extract the required information from AD and I will explain them one by one. By the way, the claims rules you see are the claims rules I have for MY environment. Your claims rules should be either exactly the same of similar to what you see below. For this part of this blog post it is also important that you also read the following blog posts to have the complete picture:

Azure AD Connect – Identifying Objects In AD And In Azure AD

REMARK: The claims rules below DO NOT take anything else into account other than just Azure AD. Therefore DO NOT use these claims rules and replace those you already have. You will need to integrate with what you already have!

REMARK: Many people rely on the groupSID claim, which is OK. There is one caviat though!. If you have many groups in AD that users are a member of, then using the groupSID may give you problems with either ADFS and/or applications. With regards to ADFS, the problem relies with the browser that is not able to put the large amount of claim types and values in the security token (cookie!). The cookie size is limited and from what I know it cannot be adjusted/increased. With regards to application the header size might be to big for the application to understand. A solution is to increase the header size of the Windows server the application is running on, but the upper limit is fixed, if you know what I mean

image

Figure 6: Claims Rules For User/Computer Authentication Against ADFS And Azure AD On the AD CP Trust

The yellow marked claim rule (rule 1):

@RuleTemplate = "PassThroughClaims"
@RuleName = "Identity Claims – Primary SID"
c:[Type == "
http://schemas.microsoft.com/ws/2008/06/identity/claims/primarysid"%5D
=> issue(claim = c);

EXPLANATION: The claim type “http://schemas.microsoft.com/ws/2008/06/identity/claims/primarysid” is required as the Windows 10 computer needs to send this to Azure AD.

The yellow marked claim rule (rule 2):

@RuleTemplate = "PassThroughClaims"
@RuleName = "Identity Claims – Windows Account Name"
c:[Type == "
http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname"%5D
=> issue(claim = c);

EXPLANATION: The claim type “http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname” is the anchor claim type required by ADFS and is also required by rule 4 to query for additional information from AD.

The yellow marked claim rule (rule 3)

@RuleTemplate = "PassThroughClaims"
@RuleName = "Identity Claims – upn"
c:[Type == "
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn"%5D
=> issue(claim = c);

EXPLANATION: The claim type “http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn” is required as the user needs to send this to Azure AD.

The yellow marked claim rule (rule 4):

@RuleName = "Identity Claims – Extra Identity Claims/Attributes"
c:[Type == "
http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname"%5D
=> issue(store = "Active Directory", types = ("http://temp.org/identity/claims/adObjectGuidBase64org&quot;, "http://temp.org/identity/claims/iamTECImmutableID", "http://temp.org/identity/claims/primaryGroupID&quot;), query = ";objectGUID,iamTECImmutableID,primaryGroupID;{0}", param = c.Value);

EXPLANATION: Based upon the “http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname”, additional claim types will be sourced from AD. The following will occur:

  • User object:
  • Computer object:
    • “objectGUID” attribute value in base64 format will go into the claim type “http://temp.org/identity/claims/adObjectGuidBase64org"
    • “iamTECImmutableID” attribute value is empty for computers
    • “primaryGroupID” attribute value (for computers this is 515) will go into the claim type “http://temp.org/identity/claims/primaryGroupID

REMARK: the AD attribute “iamTECImmutableID” does not exist in AD by default. For this the AD schema was extended!

Now, looking at all the claim rules displayed above, how will the transport of the values look like? Well, look at the table below!

FOR USERS

AD Attribute With Example Value Claim Types In Security Token For ADFS Claim Types In Security Token For Azure AD
“ObjectSID” (S-1-5-21-963889158-2862062032-2620964440-2076) http://schemas.microsoft.com/ws/2008/06/identity/claims/primarysid” (same value) N.A.
“msDS-PrincipalName” (XXXXXX\SOMEUSER) http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname” (same value)
“userPrincipalName” (SOMEUSER@XXXXXX.NL) http://schemas.xmlsoap.org/claims/UPN” (same value)
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn” (same value) http://schemas.microsoft.com/ws/2008/06/identity/claims/issuerid” (urn:federation:fs.xxxxxx.nl:AAD:XXXXXX.NL)
“ObjectGUID” (Base 64) (NQUKyhbeN0yLsrQ/1b6a3w==) http://temp.org/identity/claims/adObjectGuidBase64org” (same value) N.A.
“iamTECImmutableID” (NQUKyhbeN0yLsrQ/1b6a3w==) "http://temp.org/identity/claims/iamTECImmutableID" (same value) http://schemas.microsoft.com/LiveID/Federation/2008/05/ImmutableID” (same value)
"http://temp.org/identity/claims/iamTECImmutableID" (same value) “http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier" (same value)
“primaryGroupID” (513) http://temp.org/identity/claims/primaryGroupID” (same value) N.A.

FOR COMPUTERS

AD Attribute With Example Value Claim Types In Security Token For ADFS Claim Types In Security Token For Azure AD
“ObjectSID” (S-1-5-21-963889158-2862062032-2620964440-3333) http://schemas.microsoft.com/ws/2008/06/identity/claims/primarysid” (same value) http://schemas.microsoft.com/ws/2008/06/identity/claims/primarysid” (same value)
“msDS-PrincipalName” (XXXXXX\COMPUTER$) http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname” (same value) N.A.
“userPrincipalName” (Empty, N.A.) N.A. N.A.
“ObjectGUID” (Base 64) (Kx3CDrz7HkaTOjRLzQfKNw==) http://temp.org/identity/claims/adObjectGuidBase64org” (same value) http://schemas.microsoft.com/LiveID/Federation/2008/05/ImmutableID” (same value)
http://temp.org/identity/claims/adObjectGuidBase64org” (same value) http://schemas.microsoft.com/identity/claims/onpremobjectguid” (same value)
“iamTECImmutableID” (Empty, N.A.) N.A. N.A.
“primaryGroupID” (515) http://temp.org/identity/claims/primaryGroupID” (same value) N.A.
http://schemas.microsoft.com/ws/2012/01/accounttype” (DJ)
http://schemas.microsoft.com/ws/2008/06/identity/claims/issuerid” (urn:federation:fs.xxxxxx.nl:AAD:XXXXXX.NL)

Any questions or comments on this? Use the contact page to contact me or use the comments section below!

Enjoy and have fun!

Cheers,
Jorge
———————————————————————————————
* This posting is provided "AS IS" with no warranties and confers no rights!
* Always evaluate/test yourself before using/implementing this!
* DISCLAIMER:
https://jorgequestforknowledge.wordpress.com/disclaimer/
———————————————————————————————
############### Jorge’s Quest For Knowledge #############
#########
http://JorgeQuestForKnowledge.wordpress.com/ ########
———————————————————————————————

8 Responses to “(2016-12-16) Automatic Azure AD Join With ADFS v3.0 And Higher And Conditional Access – What You Really Need In Detail”

  1. Paolo said

    great article, 2 notes though: I believe that Redstone performs automatic registration and discovery of the SCP, so you don’t need any GPOs in place. Secondly, I believe RedStone moves away from the active endpoint (as it only supports username/password) in order to enable new authentication scenarios, such as Windows Hello for Business.

    Like

  2. […] read about the configurations required, see (2016-12-16) Automatic Azure AD Join With ADFS v3.0 And Higher And Conditional Access – What You R…. Look at the claims rules for user authentication on the RP trust for Azure AD and the CP trust for […]

    Like

  3. […] Due to the trust type “Domain Joined”, the device would be able to access resources configured with conditional access. If you computer joined to your AD domain, then you should use this option. You can read more about it in the following blog post (2016-12-16) Automatic Azure AD Join With ADFS v3.0 And Higher And Conditional Access – What You R…. […]

    Like

  4. […] (2016-12-16) Automatic Azure AD Join With ADFS v3.0 And Higher And Conditional Access – What You R… […]

    Like

  5. […] I started to look for information about how workplace join works for down-level devices and found this amazing article written by Jorge (Thanks a lot!) https://jorgequestforknowledge.wordpress.com/2016/12/16/automatic-azure-ad-join-with-adfs-v3-0-and-h&#8230; […]

    Like

  6. […] (2016-12-16) Automatic Azure AD Join With ADFS v3.0 And Higher And Conditional Access – What You R… […]

    Like

  7. […] Due to a known security vulnerability it is highly recommended that the WS-Trust Windows Transport Endpoints are disabled on the ADFS Proxies/WAPs (from the extranet). Be careful though that these endpoints are needed by Windows 10 and Windows Server 2016 and higher on the INTRANET side of ADFS to leverage Azure AD Domain Join, a.k.a. Hybrid Azure AD Domain Join (HAADJ). For more detail please see the section “ADFS Endpoints” in the blog post (2016-12-16) Automatic Azure AD Join With ADFS v3.0 And Higher And Conditional Access – What You R… […]

    Like

  8. […] you have read this blog post, at some point you will need to create a Service Connection Point (SCP), so that your clients know […]

    Like

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.