Access Control Policies and Issuance Authorization Rules in ADFS 4.0 – Part 2

In post “Access Control Policies and Issuance Authorization Rules in ADFS 4.0 – Part 1” we took a quick look on Access Control Policies in ADFS 4.0. We learnt that those can be a very helpful tool to grant permissions for using a Relying Party Trust.
However, in case of our request example, using Claim Rule Language together with Issuance Authorization Rules will meet the request straightforward while we would see difficulties when relying on Access Control Policies.
Here is the definition of our example:
Use Case Example:
All users who are member of any security group starting with CLOUD_ should get access to the Relying Party Trust (and get authorization for the Cloud application). If they are also member of any group starting with DE_, they should get a denial for that Relying Party Trust. Additionally, access is limited only to users who connect from inside the corporate network

Using Control Access Policies to create special access conditions where group membership based on a filter is the key to allow or deny access turns out to be difficult. In the Control Access Policy template, you can only choose specific groups from an Active Directory object picker, which is too static in our case where new security groups might be created and deleted again.

Only specific objects can be selected

 

Therefore, we use the advantage that ADFS 4.0 supports both, Access Control Policies and Issue Authorization Rules in the same farm.

How to get into the Issue Authorization Rules configuration item

When you create a new Relying Party Trust (RPT), you will noticed, that the wizard sets the “Permit everyone” Access Control Policy for your trust, but offers also to select from the list of templates and existing ones. A checkbox at the bottom gives you the option to skip the configuration of an Access Control Policy at the time of trust creation.

No Access Control Policy is set when creating the RPT

 

Right-clicking the Relying Party Trust after creation without setting an Access Control Policy still brings us to the well-known Access Control Policy selection.

Access Control Policies and Templates

 

In order to switch from Access Control Policy to the Issuance Authorization Rules menu we need to use the related Powershell Commandlet.

  1. We set a dummy policy as Access Control Policy (which does not do any harm because conditions are never met for access).
  2. We remove this Access Control Policy by setting $null.
Removing the existing Access Control Policy

 

Going back to the menu and right-clicking on the trust and selecting “Edit Access Control Policy …” will bring us a menu where we can define Issuance Authorization Rules, as we know from ADFS 3.0. Please note, that the Access Control Policy, which was cleared by our Powershell command No.2, has become a second life as Issuance Authorization Rule!

Issuance Authroization Rules visible in GUI again

 

The same is visible when retrieving the related attributes by using the Get-AdfsRelyingPartyTrust commandlet.

Get-ADFSRelyingPartyTrust shows Issuance Authorization Rules or the Access Control Policy

 

You will always have to use the Powershell Commandlet Set-AdfsRelyingPartyTrust if you want to clear an existing Access Control Policy from a Relying Party Trust. The GUI will only allow replacing policies.

Creating and placing the appropriate Issuance Authorization Rules

Once we know that we can place the rules as we know from ADFS 3.0, we can start to configure the conditions. Since we have to deal with the condition to be member of one or multiple groups that start with prefix “CLOUD_” and to be not a member of at least one group starting with prefix “DE_” at the same time, we will have to build two rules – one with an “add” statement and one with an “issue” statement.

The first rule will retrieve all the group names where the user is member and passes this information further to the second rule. This step is necessary because by default only the groups’ SIDs are part of the claim.

Rule with “add” statement to collect all token groups (group SIDs)

 

The second rule will then check for the permit group conditions (“name starts with CLOUD_”) and the deny group condition (“name starts with DE_”). Additionally, the rule checks for the presence of the “insidecorporatenetwork” claim, which exists whenever the user does not connect through public interfaces and works as incoming claim.
If there is no membership in a deny group, but membership in a permit group, and the user connects from the internal network, thus the rule will issue an authentication token (claim) finally.

Rules with “issue” statement to make conditions, filter and issue claims

 

Testing the Rule

When writing custom Issuance Authorization Rules, testing is key. If you plan to protect your production Relying Party Trust by complex access rules, you cannot go live with those without proper testing. There are several test applications around which make the outgoing claims visible and therefore easy to check.
Just assign the rules to the Relying Party Trust of the application and see if a test user can access or not (which implies a permit or deny of authentication though).
As you can see, from the screenshot below, our test user is member of two groups, that starts with “CLOUD_” in their names and he is obviously not member of a “DE_” group. We can also see that the “insidecorporatenetwork” claim is set to true which was another condition.

ADFS claim test application for installation in internal network

The fact that we can see the test application web site at all is the evidence that the user was authorized to use the Relying Party Trust and connect to the application. Mission accomplished without using Access Control Policies.

Microsoft has published a web based ADFS test application, which is called Claims X-Ray and works perfectly by mirroring the incoming claims.
You can find it here https://adfshelp.microsoft.com/Tools/ShowToolsInternal and external devices can access it, which makes it a very valuable troubleshooting tool.

Tool Factory: Release of PS-REPADMIN 1.9

PS-REPADMIN 1.9 is available now. PS-REPADMIN helps to view object metadata and attribute values in a simple table view.
We made several improvements in 1.9, especially for comparing groups with their metadata between trusted Active Directory domains. The tool also provides now an easier look on Proxy Addresses and Linked Attribute values.

PS-REPADMIN 1.9 was tested with Windows 10, Windows Server 2012 R2 and Windows Server 2016. Usage is on own risk. All rights reserved by Silverstar Consulting GmbH.
Download here to test the trial version for free.
(Note: After download, unzip the file and after that rename the .zip extension to .exe)

Full table view on attributes and their last change including group member values. The parallel listing makes it easy to compare values of objects from different domains.

Linked attributes are displayed in a separate view for easier comparison.

Download here to test the trial version for free.
(Note: After download, unzip the file and after that rename the .zip extension to .exe)

Powershell 5 in Windows Management Framework V5 Preview

Microsoft released a Preview of the Windows Management Framework V5. As in the past, this package ships with the according version of Powershell. Powershell V5 will bring interesting new Features.

Among those are:

  1. OneGet module with a set of comandlets to manage Software packages
  2. Commandlets to manage L2 Layer Network Switches

Find the introduction article for Windows Managagement Framework V5 by Jeffrey Snover here on TechNet.

You can dowload the Preview here.

However, the mixture of Powershell Versions we find at customer Environments will get wider and wider. Same is valid for modules like ActiveDirectory or SnapIns for Exchange. One will need to start with a lot of checks in the beginning of the code when a script is planned to be used universally.

How to write or migrate sidHistory with Powershell (3)

In our large scale Active Directory Cross Forest migration project, we now have migrated already 40.000 user accounts globally. Our self made scripting routine to migrate/write sidHistory into the target accounts turned out to be a robust, reliable part of the process and I feel safe now to share some experiences. We are running it on multiple migration servers around the globe as scheduled task – which you can easily call a “service” as it is running every 5 minutes.
I will write about the whole mechanism of how we automated our large scale Active Directory migration in another blog post, but will concentrate here to share our way of managing the sidHistory part.
As you know already from part 2 of this blog post, we were buidling our code on the examples that MSFT Jiri Formacek published here.

However, 2 main restrictions prevented us from using this code as is:

  1. We wanted to make sure that we really used the Domain Controller with the PDC Emulator role from source domain. Our source environment has 100+ domain controllers and the PDC role is siwtched from one DC to another DC under certain conditions. Therefore to use a fixed name for the PDC role Domain Controller was not acceptable.
  2. Our Active Directory account migration process was fully automated and it was the user who starts his/her migration not us. Therefore the requirement was given, that we only can run sidHistory migration (together with the account activation in target domain) as a continuous background service. Every session based approach would not have helped like we can find it in ADMT or Dell Migration Manager for Active Directory.
    Prepopulating sidHistory on the previously created disabled accounts in target domain was not an option, since Exchange 2010 was giving errors for disabled users with sidHistory of source active users under certain circumstances.

Solutions:
1) This was not a big thing. A small function could do the trick.

function getPDCE($domain) {
$context = new-object System.DirectoryServices.ActiveDirectory.DirectoryContext("domain",$domain)
$PDC=[System.DirectoryServices.ActiveDirectory.Domain]::GetDomain($context).PdcRoleOwner.Name
return $PDC
} 

2) This was not that easy (for us). Running our account migration script as usual – means as scheduled task with admin credentials – did not work for the sidHistory part in it since the credentials of the logged user account were not handed over to the SIDCloner routine.
All the code we could find on Jiri’s page asked for credential information interactively or would need explicit credentials in the script in another way.
Although we are packaging our Powershell Scripts into an .exe file by using Sapien Powershell Studio and could hide the password from simple file editing, putting user name and password into the script was not an acceptable way for us to go.
After testing back and forth, someone cam up with the idea of using the Windows credential manager to work around our deadlock situation.
The script would access the credential manager interface, get the credential information from there and would then pass them to the DsAddSidHistory function.
We created a function to retrieve credentials from Credential Manager store based on a very good script example to be found on Technet here.
While this seemed to be a clever way of achieving our target of having a scheduled user account activation script with sidHistory functionality, we ran into errors again. Retrieving credentials from Credential Manager by script obviously fails, when the script runs with exactly the credentials that you want to retrieve. This was true in our case, because the user account migration script was scheduled with that “big admin” account.

The solution finally was:
The user account migration script was running as a scheduled task with full admin credentials. When it came to migrate (in our project setup: activate) a user account in the target domain, it did not (could not) write sidHistory, but created an input file with username and target DC (the DC closest to the site where the user was had logged in from – remember that the user triggers his/her migration in our project).
On the same migration server a second script was scheduled with a server-local admin account. This script consists of 3 parts. First part is to check if there are new input files. Second part is to retrieve the full admin credentials from Credential manager and passing them to second part. Third part is to migrate sidHistory which succeeds because you have put all parts together for the SIDCloner routine:
PDC Emulator DC for source you have found by query.
Target DC was in file (but you can take every writable you want if replication delay does not matter).
Explicit credentials you get from Credential Manager.
Nowhere in both scripts password information is saved in clear text.

Additional Information