A minimalistic FIM AAD sync connector solution for Windows Intune


After some DirSync implementations one of my FIM customers has the need for mobile device management with Windows Intune. So it seems a perfect time to me for my first implementation of the AAD Connector for FIM 2010 R2.

The customer had the following special requirements:

  • No Password Sync, instead using SSO with ADFS
  • Minimalistic set of attributes on users in the cloud (Corporate and legal issues)
  • Manual management of which user goes into the cloud or not (by helpdesk)
  • Usage of proxy connection for all servers incl. FIM (no direct internet connect)

I searched the internet a bit for configuration of the WAAD connector, but the technical reference ends at the step of adding attribute flows and other posts are mostly for complex scenarios (hybrid, multi-forest and so on).

So once again I had to figure it out by myself and I decided to put my solution on here for this minimalistic implementation. I will skip the installation and configuration of ADFS and WAP, the Azure AD configuration and also the firewall/proxy configuration. There is a lot of documentation out there for this. Bit I will give the one or the other hint on some facts.
To setup your Azure/Intune for SSO with ADFS follow the guide in your Azure/Intune portal.

Some requirements for my complete solution, you can modify this by your need:

  • Create MV attribute “sourceAnchor” type: string(indexable) on person object
  • Create MV attribute “isCloudUser” type: boolean on person object
  • Create FIMPortal attribute “isCloudUser” type: boolean and bind it to the person resource type
  • Create IAF for “isCloudUser” in FIMService MA
  • Modify user resource RCDC to show attribute to users in Portal UI
  • Give for ex. Helpdesk users access rights to modify the “isCloudUser” attribute

In my solution it looks like that on users UI in FIM Portal:

PortalUI

You can modify this solution to automatically provision users to the cloud, for ex. all users with the mobile number attribute present, without the need of manual interaction.
You don’t have to create the isCloudUser attribute in that case and also have to modify the MV provisioning code a little bit.

Another requirement for the WAAD connector and my solution to work is the following software:

  • Microsoft Online Sign-In Assistant
  • Azure Active Directory PowerShell Module

You will have download links to the actual version in your Azure/Intune portal.
Let’s start with the configuration of WAAD connector:

1

Enter the Name/Password of the Azure Service Account you must create before configuring the WAAD connector.
Account MUST be Global Administrator.

In addition, make sure you set the “Password never expire” attribute via PowerShell on that account.

2

3

For Intune Mobile Device Management, we only need users for authenticating with their phones.

4

That’s the point the Technical Reference ends, but we go beyond.
From the Select Attributes dialog select the following attributes:

  • accountEnabled
  • cloudAnchor
  • givenname
  • surname
  • displayName
  • userPrincipalName
  • sourceAnchor (aka. immutableID)
  • usageLocation

5

Select cloudAnchor as the anchor to AzureAD.

6

7

Use sourceAnchor on MV and MA as the join criteria.
Source anchor will be generated by an advanced attribute flow in the AD MA which we will configure later, the attribute is also known as immutableID in Azure and necessary for SSO with ADFS to work.

8

Configure attribute flows like below. Keep in mind that the UPN of users must match to the Azure domain you registered in azure setup in order for SSO with ADFS to work. You must also set the usageLocation in order to give licenses to users later and the account has to be enabled in Azure AD, so setting this to true.

I used classic rules in this solution but it is very easy to change this to portal sync rules. But there will still be need for code to create the sourceAnchor attribute from objectGUID on the AD MA later in this post.

9

We stage a delete to the connected data source (Azure AD) to remove users if they don’t exists any longer in MV or if a helpdesk member decides to deprovision users from Azure (remember isCloudUser?).

10

11

Done.

 

Modify the AD MA attribute flow to create the sourceAnchor:

In order to generate the sourceAnchor (immutableID) we must add an advanced attribute import flow to out Active Directory MA which generates the sourceAnchor from the AD objectGUID.

Add the following attribute flow to your AD MA:
(CS: objectGUID -> MV:sourceAnchor Type:RulesExtension: CounstructCloudSourceAnchor)

12

Add the following code snippet to the MapAttributesForImport method of your AD-Extension code:
(This is an VB.NET example as the customers AD extension still uses VB)

Case "ConstructCloudSourceAnchor"
	'Construct Azure sourceAnchor (immutableID), important to authenticate cloud users with ADFS
	If (mventry("EmployeeID").IsPresent Then
		mventry("sourceAnchor").StringValue = Convert.ToBase64String(csentry("objectGuid").BinaryValue)
	Else
		mventry("sourceAnchor").Delete()
	End If


Implement MV extension code for provisioning:

While we are talking about code, here is another one. This code is used to provision the user accounts to the cloud. Because the customer uses the good old “mv extension router technique” here is the complete code of the extension for cloud provisioning. If you don’t have separated your MV extension code to multiple files you can include the necessary parts of the code into your solution.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.MetadirectoryServices;

namespace MVExtension_Cloud
{
    public class MVExtension_Cloud : IMVSynchronization
    {
        public MVExtension_Cloud()
        {
        }

        void IMVSynchronization.Initialize()
        {
        }

        void IMVSynchronization.Terminate()
        {
        }

        void IMVSynchronization.Provision(MVEntry mventry)
        {
            switch (mventry.ObjectType)
            {
                case "person":
                    try
                    {
                        ProvisionCloudUser(mventry, "AzureAD");
                    }
                    catch (Exception ex)
                    {
                        throw ex;
                    }
                    break;
            }
        }

        bool IMVSynchronization.ShouldDeleteFromMV(CSEntry csentry, MVEntry mventry)
        {
            throw new EntryPointNotImplementedException();
        }

        public static Boolean ValidateRequiredAttributes(MVEntry mventry)
        {
            Boolean result = false;
            List<String> requiredAttributes = new List<String>();
            List<String> missingAttributes = new List<String>();

            switch (mventry.ObjectType.ToLowerInvariant())
            {
                case "person":
                    {
                        requiredAttributes.Add("userPrincipalName");
                        requiredAttributes.Add("DisplayName");
                        requiredAttributes.Add("SourceAnchor");
                        requiredAttributes.Add("isCloudUser");
                        break;
                    }
                default:
                    {
                        break;
                    }

            }

            foreach (string attributeName in requiredAttributes)
            {
                if (!IsAttributePresentInMVEntry(mventry, attributeName))
                {
                    // If the attribute is not present, then add it to the list of missing attributes
                    missingAttributes.Add(attributeName);
                }
            }

            if (requiredAttributes.Count != 0 & missingAttributes.Count == 0)
            {
                result = true;
            }

            return result;
        }

        public static Boolean IsAttributePresentInMVEntry(MVEntry mventry, string attrName)
        {
            bool result = false;
            try
            {
                result = mventry[attrName].IsPresent;
            }
            catch (Exception)
            {
                // Don't need to do anything here.
            }
            return result;
        }

        private void ProvisionCloudUser(MVEntry mventry, string MAName)
        {
            ConnectedMA ma = mventry.ConnectedMAs[MAName];

            if (ValidateRequiredAttributes(mventry))
            {
                if (mventry["isCloudUser"].BooleanValue.Equals(true))
                {
                    if (ma.Connectors.Count == 0)
                    {
                        try
                        {
                            String sourceAnchor = mventry["sourceAnchor"].StringValue;
                            CSEntry cs = ma.Connectors.StartNewConnector("User");
                            cs.DN = ma.EscapeDNComponent(mventry["sourceAnchor"].StringValue);
                            cs["SourceAnchor"].StringValue = sourceAnchor;
                            cs.CommitNewConnector();
                        }
                        catch (Exception ex)
                        {
                            throw ex;
                        }
                    }
                }
            }

            if (ma.Connectors.Count == 1)
            {
                CSEntry csentry = ma.Connectors.ByIndex[0];

                if (!mventry["isCloudUser"].BooleanValue.Equals(true))
                {
                    csentry.Deprovision();
                }

                else if (!ValidateRequiredAttributes(mventry))
                {
                    csentry.Deprovision();    
                }
            }
        }
    }
}

Normally you can now run your syncs to start generating the sourceAnchor for all users from the AD MA, enable some users in Portal for Intune AAD sync  (isCloudUser), import the data to MV and on a sync you should see “provisioning adds” to the Azure/Intune connector.

In my customers case a requirement was that all connections have to go through a proxy server, after configuring the proxy and firewall with all necessary destinations you must configure WinHTTP for the service account of your FIM sync server.

Easiest way it to start a browser (run as…) with credentials of the service account, setting up your proxy connection and bypass domains.
Use the following netsh command after that to import this configuration to the WinHTTP settings:

Netsh import proxy source=ie

(Cmd.exe of course must be started as the FIM sync service account)

 

Add license to the synchronized users:

Last step is to give all the synchronized users the Intune license to make them manageable in the Intune portal.
I start the following PowerShell script directly after the confirming import of the scheduled run profile to give a license to all synchronized users who currently are missing one:

# Import Azure PowerShell Module
Import-Module MSOnline

# Build Credential Object for Authentication
$secpasswd = ConvertTo-SecureString "MyAzurePW" -AsPlainText -Force
$mycreds = New-Object System.Management.Automation.PSCredential ("Account@MyDomain.onmicrosoft.com", $secpasswd)

# Try to connect to MSOL-Service up to 10 times, because of temporary auth errors.
Connect-MSOLService -cred $mycreds

# Get all synchronized Azure Users without License and assign Intune License to them.
$UserList=Get-MsolUser -All -Synchronized -UnlicensedUsersOnly

Write-output "Starting Intune Licencse Activation PowerShell Script."
foreach ($user in $UserList)
{
    Set-MsolUserLicense -UserPrincipalName $user.UserPrincipalName -AddLicenses "MyOrga:INTUNE_A"
    $username=$user.UserPrincipalName
    write-output "Add License for user: $username"
}

The script is still missing one thing, the credentials have to be encrypted in a file, still work to do.
Modify Password, userPrincipalName and licenseName values to your needs.

And finally we are done, ok that’s a lot of pictures, text and code for a “minimalistic” solution, you’re right, sorry for that but I think you will not find a faster and smaller solution beside DirSync and Click,Click,Next,Finish 😉

Advertisements

About Peter Stapf
Senior Consultant Identity and Access MVP (Enterprise Mobility)

One Response to A minimalistic FIM AAD sync connector solution for Windows Intune

  1. Peter Stapf says:

    Just a small update on the solution. After I got some “Key not in Dictionary” Errors on Imports, it seems to me that there are references between user and device objects in Azure.
    So I had to select “devices” as possible objectclass as well, even if I will not synchronize them.
    After that, the error was gone

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: