Synchronizing Exchange Online/Office 365 User Profile Photos with FIM/MIM

Introduction

This is Part Two in the two-part blog post on managing users profile photos with Microsoft FIM/MIM. Part one here detailed managing users Azure AD/Active Directory profile photo. This post delves deeper into photos, specifically around Office 365 and the reason why you may want to manage these via FIM/MIM.

Background

User profile photos should be simple to manage. But in a rapidly moving hybrid cloud world it can be a lot more complex than it needs to be. The best summary I’ve found of this evolving moving target is from Paul Ryan here.

Using Paul’s sound advice we too are advising our customers to let users manage their profile photo (within corporate guidelines) via Exchange Online. However as described in this article photos managed in OnPremise Active Directory are synchronized to Azure AD and on to other Office365 services only once. And of course we want them to be consistent across AD DS, Azure AD, Exchange Online and all other Office365 Services.

This post details synchronizing user profile photos from Exchange Online to MIM for further synchronization to other systems. The approach uses a combination of Azure GraphAPI and Exchange Remote PowerShell to manage Exchange Online User Profile Photos.

The following graphic depicts the what the end goal is;

Current State

  • Users historically had a photo in Active Directory. DirSync/ADSync/AzureADConnect then synchronized that to Azure AD (and once only into Office 365).
  • Users update their photo in Office365 (via Exchange Online and Outlook Web Access)
    • the photo is synchronized across Office365 Services

Desired State

  • An extension of the Current State is the requirement to be able to take the image uploaded by users in Exchange Online, and synchronize it back to the OnPremise AD, and any other relevant services that leverage a profile photo
  • Have AzureADConnect keep AzureAD consistent with the new photo obtained from Office365 that is synchronized to the OnPrem Active Directory
  • Sync the current photo to the MIM Portal

Synchronizing Office365 Profile Photos

Whilst Part-one dealt with the AzureAD side of profile photos as an extension to an existing AzureAD PowerShell Management Agent for FIM/MIM, I’ve separated out the Office365 side to streamline it and make it as efficient as possible. More on that later. As such I’ve created a new PowerShell Management Agent specifically for Office365 User Profile Photos.

I’m storing the Exchange Online photo in the MIM Metaverse as a binary object just as I did for the AzureAD photo (but in a different attribute ). I’m also storing a checksum of the photos (as I did for the AzureAD Photo, but also in a different attribute) to make it easier for comparing what is in Azure AD and Exchange Online, to then be used to determine if changes have been made (eg. user updated their profile photo).

Photo Checksum

For generating the hash of the profile photos I’m using Get-Hash from the Powershell Community Extensions.  Whilst PowerShell has Get-FileHash I don’t want to write the profile photos out to disk and read them back in just to get the checksum. That slows the process up by 25%. You can get the checksum using a number of different methods and algorithms. Just be consistent and use the same method across both profile photos and you’ll be comparing apples with apples and the comparison logic will work.

Some notes on Photos and Exchange Online (and MFA)

This is where things went off on a number of tangents. Initially I tried accessing the photos using Exchange Online Remote PowerShell.

CAVEAT 1: If your Office365 Tenant is enabled for Multi-Factor Authentication (which it should be) you will need to get the Exchange Online Remote PowerShell Module as detailed here. Chances are you won’t have full Office365 Admin access though, so as long as the account you will be using is in the Recipient Management Role you should be able to go to the Exchange Control Panel using a URL like https://outlook.office365.com/ecp/?realm=<tenantname>&wa=wsignin1.0 where tenantname is something like customer.com.au From the Hybrid menu on in the right handside pane you will then be able to download the Microsoft.Online.CSE.PSModule.Client.application I had to use Internet Explorer to download the file and get it installed successfully. Once installed I used a few lines from this script here to load the Function and start my RPS session from within PowerShell ISE during solution development.

CAVEAT 2: The EXO RPS MFA PS Function doesn’t allow you to pass it your account password. You can pass it the identity you want to use, but not the password. That makes scheduled process automation with it impossible.

CAVEAT 3: The RPS session exposes the Get-UserPhoto cmdlet which is great. But the RPS session leverages the GraphAPI. The RPS PS Module doesn’t refresh it’s tokens, so if the import takes longer than 60 minutes then using this method you’re a bit stuffed.

CAVEAT 4: Using the Get-UserPhoto cmdlet detailed above, the syncing of photos is slow. As in I was only getting ~4 profile photos per minute slow. This also goes back to the token refresh issue as for pretty much any environment of the size I deal with, this is too slow and will timeout.

CAVEAT 5: You can whitelist the IP Address (or subnet) of your host so MFA is not required using Contextual IP Addressing Whitelisting. At that point there isn’t really a need to use the MFA Enabled PREVIEW EXO RPS function anyway. That said I still needed to whitelist my MIM Sync Server(s) from MFA to allow integration into the Graph API. I configured just the single host. The whitelist takes CIDR format so that looks like /32 (eg. 11.2.33.4/32)

Performance Considerations

As I mentioned above,

  • using the Get-UserPhoto cmdlet was slow. ~4 per minute slow
  • using the GraphAPI into Exchange Online and looking at each user and determining if they had a photo then downloading it, was also slow. Slow because at this customer only ~50% of their users have a photo on their mailbox. As such I was only able to retrieve ~145 photos in 25 minutes. *Note: all timings listed above were during development and actually outputting the images to disk to verify functionality. 

Implemented Solution

After all my trial and error on this, here is my final approach and working solution;

  1. Use the Exchange Online Remote PowerShell (non-MFA version) to query and return a collection of all mailboxes with an image *Note, add an exception for your MIM Sync host to the white-listed hosts for MFA (if your Office365 Tenant is enabled for MFA) so the process can be automated
  2. Use the Graph API to obtain those photos
    • with this I was able to retrieve ~1100 profile photos in ~17* minutes (after ~2 minutes to query and get the list of mailboxes with a profile photo)

Pre-requisites

There’s a lot of info above, so let me summarize the pre-requisties;

  • The Granfeldt PowerShell MA
  • Whitelist your FIM/MIM Sync Server from MFA (if your Office 365 environment is enabled for MFA)
  • Add the account you will run the MA as, that will in turn connect to EXO via RPS to the Recipient Management Role
  • Create a WebApp for the PS MA to use to access users Profile Photos via the Graph API (fastest method)
  • Powershell Community Extensions to generate the image checksum

Creating the WebApp to access Office365 User Profile Photos

Go to your Azure Portal and select the Azure Active Directory Blade from the Resource Menu bar on the left. Then select App Registrations and from the Manage Section of the Azure Active Directory menu, and finally from the top of the main pane select “New Application Registration“.

Give it a name and select Web app/API as the type of app. Make the sign-in URL https://localhost and then select Create.

Record the ApplicationID that you see in the Registered App Essentials window. You’ll need this soon.

Now select All Settings => Required Permissions. Select Read all users basic profiles in addition to Sign in and read user profile. Select Save.

Under Required Permissions select Add and then select 1 Select an API, and select Office 365 Exchange Online then click Select.

Choose 2 Select Permissions and then select Read user profiles and Read all users’ basic profiles. Click Select.

Select Grant Permissions

From Settings select Keys, give your key a Description, choose a key lifetime and select Save. RECORD the key value. You’ll need this along with the WebApp ApplicationID/ClientID for the Import.ps1 script.

Using the information from your newly registered WebApp, we need to perform the first authentication (and authorization of the WebApp) to the Graph API. Taking your ApplicationID, Key (Client Secret) and the account you will use on on the Management Agent (and that you have assigned the Recipient Management Role in Exchange Online) and run the script detailed in this post here. It will authenticate you to your new WebApp via the GraphAPI after asking you to provide the account you will use on the MA and Authorizing the permissions you selected when registering the app. It will also create a refresh.token file which we will give to the MA to automate our connection. The Authorization dialog looks like this.

Creating the Management Agent

Now we can create our Management Agent using the Granfeldt PowerShell Management Agent. If you haven’t created one before checkout a post like this one, that further down the post shows the creation of a Granfeldt PSMA. Don’t forget to provide blank export.ps1 and password.ps1 files on the directory where you place the PSMA scripts.

PowerShell Management Agent Schema.ps1

PowerShell Management Agent Import.ps1

As detailed above the PSMA will leverage the WebApp to read users Exchange Profile Photos via the Graph API. The Import script also leverages Remote Powershell into Exchange Online (for reasons also detailed above). The account you run the Management Agent as will need to be added to the Recipient Management Role Group in order to use Remote PowerShell into Exchange Online and get the information required.

Take the Import.ps1 script below and update;

  • Update lines 11, 24 and 42 for the path to where you have put your PSMA. Mine is under the Extensions directory in a directory named EXOPhotos.
  • copy the refresh.token generated when authenticating and authorizing the WebApp earlier into the directory you specified in line 42 above.
  • Create a Debug directory under the directory you specified in lines 11,24 and 42 above so you can see what the MA is doing as you implement and debug it the first few times.
  • I’ve written the Import to use Paged Imports, so make sure you tick the Paged Imports checkbox on the configuration of the MA
  •  Update Lines 79 and 80 with your ApplicationID and Client Secret that you recorded when creating your WebApp

Running the Exchange User Profile Photos MA

Now that you have created the MA, you should have select the EXOUser ObjectClass and the attributes defined in the schema. You should also create the EXOPhoto (as Binary) and EXOPhotoChecksum (as String) attributes in the Metaverse on the person ObjectType (assuming you are using the built-in person ObjectType).

Configure your flow rules to flow the EXOPhoto and EXOPhotoChecksum on the MA to their respective attributes in the MV.

Create a Stage Only run profile and run it. If you have done everything correctly you will see photos come into the Connector Space.

Looking at the Connector Space, I can see EXOPhoto and EXOPhotoChecksum have been imported.

After performing a Synchronization to get the data from the Connector Space into the Metaverse it is time to test the image that lands in the Metaverse. That is quick and easy via PowerShell and the Lithnet MIIS Automation PowerShell Module.

$me = Get-MVObject -ObjectType person -Attribute accountName -Value "drobinson"
$me.Attributes.EXOPhoto.Values.ValueBinary
[System.Io.File]::WriteAllBytes("c:\temp\myOutlookphoto.jpg" ,$me.Attributes.EXOPhoto.Values.ValueBinary )

The file is output to the directory with the filename specified.

Opening the file reveals correctly my Profile Photo.

Summary

In Part one we got the AzureAD/Active Directory photo. In this post we got the Office365 photo.

Now that we have the images from Office365 we need to synchronize any update to photos to Active Directory (and in-turn via AADConnect to Azure AD). Keep in mind the image size limits for Active Directory and that we retrieved the largest photo available from Office365 when synchronizing the photo on. There are a number of PowerShell modules for photo manipulation that will allow you to resize accordingly.

A quick start guide to leveraging the Azure Graph API with PowerShell and oAuth 2.0

Introduction

In September 2016 I wrote this post detailing integrating with the Azure Graph API via PowerShell and oAuth 2.0.

Since that point in time I’ve found myself doing considerably more via PowerShell and the Graph API using oAuth. I regularly find myself leveraging previous scripts to generate a new script for the initial connection. To the point that I decided to make this simpler and provide a nice clean starting point for new scripts.

This blog post details a simple script to generate a couple of PowerShell Functions that can be the basis for integration with Graph API using PowerShell via a WebApp using oAuth2.

Overview

This script will request the necessary information required to call into the Graph API and establish a session. Specifically;

Armed with this information the shell of a PowerShell script will be created that will;

  • Authenticate a user to Graph API via Powershell and oAuth 2.0
  • Request Authorization for the WebApp to access the Scope provided (if Admin approval scope is requested and the AuthN is performed by a non-admin an authorization failure message will appear detailing an Administrator must authorize).
  • Obtain and Authorization Code which will contain the Bearer Token and Refresh Token.
    • The Bearer token can be used to make Graph API calls for up to 1 hour.
    • The Refresh token will allow you to request a new token and allow your script to be used again to interact via Graph API without going through the Authentication process again.

The following graphic shows this flow.

Create/Register your Application

Go to the Application Registration Portal https://apps.dev.microsoft.com/ and sign in. This is the new portal for registering your apps. It will show any previous apps you registered within AzureAD and any of the new “Converged Apps” you’ve created via the new Application Registration Portal.

Select Add an app from the Converged applications list.

Give your app a name and select Create

Record the Application ID (previously known as the Client ID) and select Generate New Password.

You will be provided your Client Secret. Record this now as it is the only time you will see it. Select Ok.

By default you will get User.Read permissions on the API. That is enough for this sample. Depending on what you will do with the API you will probably need to come and change the permissions or do it dynamically via the values you supply the $resource setting in your API calls.

Select Platforms, select Web and add a reply URL of https://localhost

Scroll to the bottom of the Registration windows and select Save.

Generate your PowerShell Graph API oAuth Script

Copy the following script and put it into an Administrator PowerShell/PowerShell ISE session and run it.

It will ask you to choose a folder to output the resultant PowerShell Script to. You can create a new folder through this dialog window if require.

The script will prompt you for the Client/Application ID, Client Secret and the Reply URL you obtained when registering the Web App in the steps above.

The script will be written out to the folder you chose in the first step and it will be executed. It will prompt you to authenticate. Provide the credentials you used when you created the App in the Application Registration Portal.

You will be prompted to Authorize the WebApp. Select Accept

If you’ve executed the previous steps correctly you’ll receive an AuthCode in your PowerShell output window

You’ll then see the output for a sample query for your user account and below that the successful call for a refresh of the tokens.

Summary

In the folder you chose you will find a PowerShell script with the name Connect-to-Microsoft-Graph.ps1You will also find a file named refresh.token. You can use the script to authenticate with your new app, but more simply use the Get-NewTokens function to refresh your tokens and then write your own API queries to your app using the tokens. Unless you change the scope you don’t need to run Get-AzureAuthN again. Just use Get-NewTokens before your API calls.

e.g

Get-NewTokens  
$myManager = Invoke-RestMethod -Method Get -Headers @{Authorization = "Bearer $accesstoken"
 'Content-Type' = 'application/json'} `
 -Uri "https://graph.microsoft.com/v1.0/me/manager"

 $myManager

Change the scope of your app to get more information. If you add a scope that requires Admin consent (and you’re not an admin), when prompted to authenticate you will need to get an Admin to authenticate and authorize the scope. Because you’ve changed the scope you will need to run the Get-AzureAuthN function again after updating $scope (as per below) and the dependent $scopeEncoded.

As the screen shot below shows I added the Mail.Read permission. I changed the $scope in the script so that it reflected the changes e.g

#Scope
$scope = "User.Read Mail.Read"
$scopeEncoded = [System.Web.HttpUtility]::UrlEncode($scope)

When running the script again (because of the change of scope) you will be prompted to confirm the change of access.

You can then query your inbox, e.g.

 $myMail = Invoke-RestMethod -Method Get -Headers @{Authorization = "Bearer $accesstoken"
 'Content-Type' = 'application/json'} `
 -Uri "https://graph.microsoft.com/v1.0/me/messages"
 $mymail

And there is mail messages from your inbox.

I hope that makes getting started with the oAuth2 Graph API via PowerShell a lot simpler than it was for me initially, with the differing endpoints, evolving API and the associated documentation somewhere in-between.

Using the Lithnet PowerShell Modules to generate full object metadata FIM/MIM HTML Reports

How many times have you wanted a consolidated report out of FIM/MIM for an object? What connectors does it have, what are the values of the attributes, which Management Agent contributed the value(s) and when? Individually of course you can get that info using the Metaverse Search and looking at the object in MIM Portal. But what if you wanted it all with a single query? This blog post provides an approach to doing just that. The graphic above shows a screenshot of a sample output. Click this Sample Report for full resolution version of the screenshot above. Note: The updated version of the script below outputs DisplayName for the ExpectedRulesList attribute so it actually provides valuable information. 

Overview

The approach is quite simple. It is;

  • Query the FIM/MIM Metaverse for an object
  • Take the response from the Metaverse to build the Connectors and Metaverse Hologram reports
  • Use the connector information to query the MIM Service MA (this example assumes it is on the same server. If not add the following line into the script with the appropriate values) and get the objects MIM Service Connector Space info
    Set-ResourceManagementClient -BaseAddress http://fimsvc:5727;
  • Take information retrieved above to then query the MIM Service and return the information for the object.
  • Format all the output for HTML, apply a simple style sheet, output to file and display in the default browser

NOTE: If you combine this with the Get-MVObject query building script detailed here it can be a relatively simple solution. That script even uses the same variables $queries and $query as outputs from the search and input into the HTML Report.

NOTE: You could possibly run it remotely from the MIM Sync Server too, if you leverage Remote Powershell to your FIM/MIM Sync server as detailed here.

The Script

Here it is. Lines 23 and 24 contain a hard-coded query. Update for your search criteria, or as detailed above combine this with the Get-MVObject query building script detailed here .  The Output directory specified in Line 7 is where the stylesheet and the resultant HTML file will be placed. Update for your needs.

For the Expected Rules List (unlike the screenshot as I’ve modified the script afterwards), the script gets the DisplayName for them and puts that in the report. DisplayName is more valuable than an ERE ObjectID.

Scripting queries for Lithnet Get-MVObject searches into the Microsoft Identity Manager Metaverse

It probably seems obvious by now, but I seem to live in PowerShell and Microsoft Identity Manager. I’m forever looking into the Microsoft Identity Manager Metaverse for objects.

However, sometimes I get tripped up by the differences in Object Classes between the FIM/MIM Service and the Metaverse, the names of the Object Classes (obviously not Person, Group and Contact) and in situations where they are case-sensitive.  If you’re using the Sync Service Manager Metaverse Search function though you get a pick list. But getting the data out to do something else with isn’t an option.

Solution

I’ve looked to quickly provide a similar function to the pick lists in the Metaverse Search GUI via Powershell which then gets executed by the Get-MVObject PowerShell Module.

UPDATE: 17 May 2017 The Lithnet MIIS Automation PowerShell Module has been updated for Get-MVObject to support the ObjectType Scope. I’ve updated the script to include the scope parameter based on the ObjectClass selected at the beginning of the script. 

I’ve defaulted the ObjectClass to Person so you can just press enter. But if you have custom ObjectClasses in your Metaverse you may need to change the index number in Line 48 from 5 to whichever index Person appears in your environment. Same goes for the default attribute of AccountName in the Attribute list. It appears at index 5 (Line 77) in my attribute list.

Process

Basically just run the PowerShell script and choose your options. The script needs interaction with the FIM/MIM Sync server, so you run it from the FIM/MIM Sync server. If you want to run it remotely (of course you do), then Remote PowerShell is your friend. Checkout how to do that to the FIM/MIM Server in this post here.

The Script itself will query the FIM/MIM MV Schema and return a list of Object Classes. As detailed above, in Line 48 of the script I have ‘index 5’ as the default which in my environment is Person and as such you can just hit enter if that is the Object Class you want to choose attributes from in the next step. Otherwise type the name of the ObjectClass you want. You don’t have to worry about case sensitivity as the script handles that. You can only choose a single ObjectClass obviously, but the menu ui I’ve used allows for multiple selections. Just press enter when prompted for another option for ObjectClass.

You’ll then be presented with a list of attributes from the chosen Object Class above. Again as detailed above I have it defaulting to ‘accountName’ which is index 5 in my list. Change (Line 77) for the default you want. This means you can just hit enter if accountName is what you’re querying on (which is common). Or choose another option. This then also allows you to also choose multiple attributes (which will be added to an array). This means you can use this for complex queries such as;

accountName startsWith 'dar'
sn startsWith 'rob'
mail contains '@kloud'

If you want to choose multiple attributes for your query and one of them is the default option, make sure you specify one of the attributes that is not the default first so that you get the option to specify more. When you’ve chosen all the attributes you are going to use in your query hit enter and the script will take an empty response as the end of your choices.

Now for each attribute chosen you will be prompted for an Operator. Pretty simple. Just choose from the available options. Note: all operators are shown but not all operators can be used for all attribute types. e.g. Don’t select ‘EndsWith’ for a Boolean attribute type and expect it to work. If you choose an operator other than the default (equals in my example) hit enter when prompted for the second time and the script will take an empty response as the end of your choices.

Finally provide what you the value is for the search term for the attribute. If the value has spaces, don’t worry about putting the value in quotes. The script takes care of that.

The last two steps will iterate through, for queries where you have chosen multiple attributes.

And you’re done. $query is the variable that contains the results. In line 115 I’m using Show-Object from the PowershellCookBook PSM. That then gives you a GUI representation of the result as shown below. If the query returns multiple results this will only show the last.

Line 114 outputs the value of the attributes ($query.attributes) to the console as well. If you have multiple objects returned $query will show them as shown below.

Finally if you want to run the query again, or just make a subtle change, you shouldn’t have to go through that again. Get the value of $querytxt and you’ll get the query and the command to execute it. $querytxt is also output to the console as shown below. Copy and paste it into Powershell ISE, update and execute.

The Script

Here is the raw script. Hardly any error handling etc, but enough to get you started and tailor it for your requirements. Enjoy.

Diagnosing FIM/MIM ‘kerberos-no-logon-server’ error on an Active Directory Management Agent

Overview

I have a complex customer environment where Microsoft Identity Manager is managing identities across three Active Directory Forests. The Forests all serve different purposes and are contained in different network zones. Accordingly there are firewalls between the zone where the MIM Sync Server is located and two of the other AD Forests as shown in the graphic below.

As part of the project the maintainers of the network infrastructure had implemented rules to allow the MIM Sync server to connect to the other two AD Forests. I had successfully been able to create the Active Directory Management Agents for each of the Forests and perform synchronization imports.

The Error ‘kerberos-no-logon-server’

Everything was going well right up to the point I went to export changes to the two AD Forests that were separated by firewalls. I received the ‘kerberos-no-logon-server’ error as shown below from the run profile output.

I started investigating the error as I hadn’t encountered this one before. There were a few posts on the possibilities mainly dealing with properties of the AD MA’s configuration. But I did stumble on a mention of kerberos being used when provisioning users to Active Directory and setting the initial password. That aligned with what I was doing. I had provided the networking engineers with my firewall port requirements. Those are (no PCNS required for this implementation) ;

  • 389 TCP – LDAP
  • 636 TCP – LDAPS
  • 88 TCP – Kerberos
  • 464 TCP/UDP – Kerberos
  • 53 TCP – DNS
  • 3268 TCP/UDP – Global Catalog
  • 3269 TCP/UDP – Global Catalog
  • 135 TCP – RPC

My old school immediate thought was to Telnet to each of the ports to see if the firewall was allowing me through. But with a couple of forests to test against and UDP ports as well, it wasn’t going to be that easy. I found a nice little Test-Port function that did both TCP and UDP. I already had an older script for testing TCP ports via PowerShell. So I combined them.

Identifying the cause

As suspected connectivity to the forest where my MIM Sync Server was located was all good. The other two, not so much. GC connectivity wouldn’t give me the Kerberos error, but not having Kerberos Port 464 certainly would.

In the backwards and forwards with the networking team I had to test connectivity many times so I added a running output as well as a summary output. The running output highlighting ports that weren’t accessible.

Here’s the raw script if you’re in a similar situation. Get the Test-Port Function from the URL in line 1 to test UDP Port connectivity. Add additional ports to the arrays if required (eg. for PCNS), and update the forest names in lines 21-23.

Summary

I’m sure this is going to become more relevant in a Cloud/Hybrid world where MIM Servers will be in Azure, Active Directory Forests will be in different networks and separated by firewalls and Network Security Groups.

An alternate method for dealing with Orphaned MetaVerse Objects

Update 21 April ’17. The LithnetMIISAutomation PS Module now has a -Force switch for Delete-CSObject

As often happens in development environments, data changes, configurations change and at some point you end up with a whole bunch of objects that are in no-mans land. This happened to me today. I had thousands of objects that we basically empty but had previously triggered to be exported to the MIM Service prior to them actually being deleted from the source management agent.

An example of one of the objects. A group with a Pending Export to the MIM Service.

A closer look at the object and there is no attribute data present as the source object had been removed.

And only a single connector, to the MIM Service which it will never reach as it doesn’t contain the mandatory attributes.

Normally to clean up such a mess you’d probably be looking at deleting the Connector Space for the MIM Service and then refreshing it from the MIM Service and these objects would be gone. However, this development environment is rather large, and that wasn’t something I had time or was prepared for at this time. So here’s how I worked around the issue.

Deleting spurious objects from the Connector Space

There’s two approaches;

  1. Select each of the errors, select the MIM Service Connector and select delete. That would work but I had thousands.
  2. Automate the process described in point 1. That’s the approach I took

Using the ever versatile Lithnet MIM Sync Powershell Module I retrieved the last run details for my MIM Service MA. I grabbed all the errors, inspected the errors for the ones that were failing creation to the MIM Service and then deleted the CSObject for that orphan.

Here’s where it got more than a little clink clink cowboy-ish. The Delete-CSObject cmdlet requires confirmation to delete the CSObject. There is not a switch to force the delete, or accept confirmation globally*. I wasn’t going to click Yes or press Enter 5000 times either.

So I wrote a small script that loops and checks for the Confirm disconnection dialog and sends the enter key to window.

Here’s the two little scripts.

This first script retrieves the last run details and loops through the errors.

This second script which I ran in a second separate PowerShell Runspace loops around and presses enter at the right time.

*I’ve submitted an enhancement request to Ryan to add a confirm parameter to Delete-CSObject

Adapting to the changes in the AzureAD Preview PowerShell Module ADAL Helper Library

I’m a big proponent of using PowerShell for integration and automation of Azure Active Directory Services using the Azure AD GraphAPI. You may have seen many of my posts leverage the evolving Azure AD Preview PowerShell Module helper libraries. Lines in my scripts that use this look like the one below. In this case using preview version 2.0.0.52.

# the default path to where the ADAL GraphAPI PS Module puts the Libs
Add-Type -Path 'C:\Program Files\WindowsPowerShell\Modules\AzureADPreview\2.0.0.52\Microsoft.IdentityModel.Clients.ActiveDirectory.dll'

The benefit of using this library is the simplification of Authentication to AzureAD, from which we can then receive a token and interact with the GraphAPI via PowerShell using Invoke-RestMethod.

Earlier this week it was bought to my attention that implementation of some of my scripts were failing when using the latest v2 releases of the AzureAD PowerShell Module (v2.0.0.98).  Looking into it the last version I had working is v2.0.0.52. v2.0.0.55 doesn’t work with my scripts either.  So anything after v2.0.0.52 the following will not work

What’s Changed?

First up the PowerShell Module has been renamed. It is no longer AzureADPreview, it is just AzureAD. So the path it gets installed into (depending on the version you have) is now;

'C:\Program Files\WindowsPowerShell\Modules\AzureAD\2.0.0.98\Microsoft.IdentityModel.Clients.ActiveDirectory.dll'

Looking into the updated PowerShell Module there has been a change to the Microsoft.IdentityModel.Clients.ActiveDirectory.dll library.

A number of the methods in the library have changed. I believe this is part of Microsoft transitioning the endpoint to use GraphAPI. With that understanding I approached using PowerShell to integrate with the GraphAPI more akin to the way I do when not using the helper library.

User PowerShell and the ADAL Helper Library to connect to AzureAD via the GraphAPI

Here is the updated script to connect (and retrieve a batch of users). You will need to update lines 4, 17 & 18 for your Tenant name and the username and password (non-MFA enabled) you will be connecting with.

Bulk create and update related configuration objects in FIM/MIM using the Lithnet Import-RMConfig cmdlet

Working on a FIM implementation for a customer, I needed to bulk create and update a number of related Sets and MPRs which granted permissions to users. I could have performed this task a number of ways:

  • Manually create and update all objects
  • Scripted in PowerShell using FIM Automation
  • Scripted using the Lithnet FIM/MIM Service PowerShell Module

I’ve been successfully using the Lithnet FIM/MIM Service PowerShell Module in a number of scripts to query and bulk create objects in the FIM Service which has greatly improved the quality and simplicity of my PowerShell scripts compared to using the FIM Automation module. What I hadn’t used to date was the ability in the Lithnet module to use the Configuration Management features within FIM, so I thought I’d set myself a challenge to come upto speed with this feature and determine if it was a quicker method than using FIM Automation. I knew it was going to be quicker using the Lithnet module but since I needed to do some research and testing compared to knowing how to tackle it using FIM Automation, I needed to be 100% convinced.

This blog assumes a very good working knowledge of the Microsoft Forefront Identity Manager product, now known as Microsoft Identity Manager. If you haven’t used the Lithnet module yet, what are you waiting for! Ryan Newington has developed and released to the community this PowerShell module which cuts the amount of scripting required to manage FIM configuration and objects within the FIM Service. Using the Configuration Management features was a perfect set of functions I needed for my scenario which I will now describe.

Scenario

For this customer, they had a delegated permissions model where each of their 13 agencies had administrators that were able to manage a fixed set of user attributes just for their own users. Users from other agencies were not visible within their view within the FIM Portal, and to support this model, each agency has linked FIM Set and MPR objects to give this delegated administration. My customer engagement was to update this existing FIM implementation to add custom telephone-based attributes against each user, and create a new phone object type. The existing permissions needed to be updated to include the new telephony attributes, and new permissions were required for the phone objects linked to each agency. This customer had 13 agencies and the beauty was the naming convention the FIM solution had for Sets and MPRs, with the names of the common objects being the same with only the agency name being different. For example, each agency administrator Set object was called ‘_Agency (<agency>) Administrators’. This was the same for MPRs, so my task was made easier as I just need to develop a templated XML configuration file which I could easily update per agency. Follow me so far?

What I liked about the Configuration Management feature was the ability to easily reference other objects without needing to separately query the FIM Service to return an ‘ObjectID’ which you can then supply to your configuration. The Lithnet module takes care of the referencing.

The Lithnet module is located here, and the quickest way to learn about this Configuration Management feature is to read the Lithnet documentation here, and as suggested watch the recording from FIM contributor Ike Ugochuku and download his sample XML configuration here which is a great starting point – here.

Required Actions

For my scenario, I needed to perform the following:

  • Create a Set object to group all phone objects for each agency
  • Create a permissions-granting MPR object giving each agency Modify permissions to one attribute for phone objects, a ‘visibility’ boolean attribute. You’ll notice in the XML configuration below, the Set created above is referenced, showing the power of this Lithnet module
  • Create a permissions-granting MPR object giving each agency Read permissions to all attributes for phone objects
  • Update an existing permissions-granted MPR object giving each agency Modify permissions to one attribute for user objects, against the ‘visibility’ attribute
  • Update an existing permissions-granted MPR object giving each agency Read permissions to the new phone related attributes for user objects
  • Update an existing permissions-granted MPR object removing the Modify permissions for user self-service which has been assigned in error some time ago

Using the -Preview switch against a configuration XML file (sample listed below) for the first agency, a preview can be tested before applying.

On a second agency with some of the updates already applied, I performed a preview. It shows all objects exist and which individual change would be applied.

As a test against the second agency, I changed resource operation from ‘Add Update’ to ‘Add’. When attempting the preview again, it showed it is creating the Sets even though they already exist. This is not ideal for verification, so I changed it back to ‘Add Update’ which I recommend using.

Removing ‘Preview’ and committing the change against the first agency shows what is changed and committed to FIM.

Validating the changes with a preview once applied to confirm all changes had been committed.

Although I could have been smarter to script the process for the remaining agencies, I did a search-and-replace of the agency name in the configuration XML file, saved it and re-ran the import.

Last step was to check the FIM Service requests to confirm they have been completed.

My sample XML configuration file for an agency change is located here.

Tips

Some simple recommended tips based on my use of this Configuration Management feature:

  • ID is a required tag even if you don’t use it as a reference within your XML configuration file
  • When creating FIM objects, use the XML tag ‘Add Update’ so that the Preview function can be used to validate the changes you have performed. If you leave it as ‘Add’, the Preview will simply report that the object ‘would’ be created if executed, rather than query and check that the objects exists and no update is required
  • Case sensitivity of the XML tags as mentioned by Ike in his video. I didn’t test changing the case as the sample XML configuration files were working configuration

So how long did it all take?

My goal was to make the changes into production against the 13 agencies after successfully testing the FIM configuration in a test environment. After watching Ike’s video and reading Ryan’s wiki, it took about 15 minutes to customise the sample XML files and update the configuration in the production FIM instance for all 13 agencies. A huge time saving. Other colleagues have started using this functionality both for speed of implementation and consistency in configuration, and my colleague Darren Robinson has also saved a great deal of effort as detailed in his blog here.

What scenarios have you performed with this functionality? Let us know in the comments below.

Joining Identities between Active Directory and Azure Active Directory using Microsoft Identity Manager

Introduction

One of the foundations of Identity Management is the ability to join an identity between disparate connected systems. As we extend our management of identities into cloud services this adds a few twists.

A key concept is to use an anchor that is persistent. Something that doesn’t change through a users life-cycle. A user’s Security IDentifier (SID) in Active Directory is perfect. It doesn’t change when a user or group may get renamed.  What gets interesting is how the SID is represented when returned using different methods. That is what I quickly cover in this post.

Overview

The defacto method for connecting your OnPremise Active Directory to Azure Active Directory is to use Azure Active Directory Connect. AADC will synchronise users and groups SID’s to the corresponding object in AAD into the onPremisesSecurityIdentifier attribute. 

When the onPremisesSecurityIdentifier  attribute is retrieved via the GraphAPI the format looks like this: S-1-5-21-3878594291-2115959936-132693609-65242

Using an Active Directory Management Agent for FIM/MIM and synchronizing the objectSID from the OnPremise AD represents the value in the Metaverse in binary format which when viewed as text looks like this:  15000005210002431664623112825230126105190232781820

Translating SID formats so we can join identities

What we need to do is translate the string representation of the SID returned from the GraphAPI and AzureAD so that we have it in a binary format. Then we can then use those attributes in join rules to match users/groups between AzureAD and our OnPremise Active Directory.

 

In my environments I’m using the out of the box FIM/MIM Active Directory Management Agent. For Azure AD/Office 365 I’m using the Granfeldt PowerShell Management Agent to integrate with Azure AD via the GraphAPI.

overview1

On my AzureAD PowerShell Management Agent I have an attribute named AADonPremiseSID configured with the format as Binary in my PSMA Schema.ps1 as shown below.

$obj | Add-Member -Type NoteProperty -Name "AADonPremiseSID|Binary" -Value 0x10

On my Azure AD PSMA I have the following lines in my Import.ps1 which essentially takes the value retrieved from the GraphAPI S-1-5-21-3878594291-2115959936-132693609-65242 and converts it to a binary array that in text looks something like 15000005210002431664623112825230126105190232721825400 and stores it in the AADonPremiseSID binary attribute in the connector space.

# Create SID .NET object using SID string from AAD S-1-500-........ 
$sid = New-Object system.Security.Principal.SecurityIdentifier $user.onPremisesSecurityIdentifier
 
#Create a byte array for the length of the users SID
$BinarySid = new-object byte[]($sid.BinaryLength)

#Copy the binary SID into the byte array, starting at index 0
$sid.GetBinaryForm($BinarySid, 0)

#Add the SID to the user in the connector space
$obj.Add("AADonPremiseSID",$BinarySid)

This then lets me join my users (and groups using the same method) between AD and AAD.  Essentially a line to put it into Security Identifier format, two lines to convert it to a binary array and a line to store it in the connector space. Simple when you don’t over think it.

I’m posting this because I know I’m going to need to do this often. Hope it helps someone else too.

Send mail to Office 365 via an Exchange Server hosted in Azure

Those of you who have attempted to send mail to Office 365 from Azure know that sending outbound mail directly from an email server hosted in Azure is not supported due to elastic nature of public cloud service IPs and the potential for abuse. Therefore, the Azure IP address blocks are added to public block lists with no exceptions to this policy.

To be able to send mail from an Azure hosted email server to Office 365 you to need to send mail via a SMTP relay. There is a number of different SMTP relays you can utilise including Exchange Online Protection, more information can be found here: https://blogs.msdn.microsoft.com/mast/2016/04/04/sending-e-mail-from-azure-compute-resource-to-external-domains

To configure Exchange Server 2016 hosted in Azure to send mail to Office 365 via SMTP relay to Exchange Online protection you need to do the following;

  1. Create a connector in your Office 365 tenant
  2. Configure accepted domains on your Exchange Server in Azure
  3. Create a send connector on your Exchange Server in Azure that relays to Exchange Online Protection

Create a connector in your Office 365 tenant

  1. Login to Exchange Online Admin Center
  2. Click mail flow | connector
  3. Click +
  4. Select from: “Your organisation’s email server” to: “Office 365”o365-connector1
  5. Enter in a Name for the Connector | Click Nexto365-connector2
  6. Select “By verifying that the IP address of the sending server matches one of these IP addresses that belong to your organization”
  7. Add the public IP address of your Exchange Server in Azureo365-connector3

Configure accepted domains on your Exchange Server in Azure

  1. Open Exchange Management Shell
  2. Execute the following PowerShell command for each domain you want to send mail to in Office 365;
  3. New-AcceptedDomain -DomainName Contoso.com -DomainType InternalRelay -Name Contosoaccepted-domain1

Create a send connector on your Exchange Server in Azure that relays to Exchange Online Protection

  1. Execute the following PowerShell command;
  2. New-SendConnector -Name “My company to Office 365” -AddressSpaces * -CloudServicesMailEnabled $true -RequireTLS $true -SmartHosts yourdomain-com.mail.protection.outlook.com -TlsAuthLevel CertificateValidationsend-connector1