Provisioning two Office365 tenants from one AD: Making it work with Dirsync

[ UPDATE:  A new version of Dirsync was released in June 2013 which uses the username MSOL_AD_SYNC_ followed by a random hex value in order to make the username unique.  This helps with the two-accounts-with-the-same-username issue but the basic principle of filtering/scoping the two Dirsyncs still applies so I’m leaving this post “up”. ]

I recently assisted an educational customer who had two independent live@EDU tenants with upgrades to Office365.  They were previously provisioning users into the two tenants using two separate management agents on an old MIIS 2003 server  and keep all users in a single AD.  Therefore, we needed to find a way to make things work using only the Dirsync tool provided by Microsoft.

Dirsync is basically a preconfigured version of FIM 2010 which has two management agents in it:  one to connect to an AD and another to connect to Office 365/Azure AD.  What we needed to figure out was how to get two Dirsync installations to cooperate while working from a single source AD.

Here’s how I approached the problem.

1)  How to allow two Dirsync installations to connect to the same source AD:

Getting two copies of Dirsync to both talk to the same AD is easy.  When you run the Dirsync configuration wizard, it creates a domain account called MSOL_AD_Sync which is assigned a randomized password.  Obviously, running the installer for Dirsync #2 overwrites the password used by Dirsync #1 so the solution was to set up Dirsync #1, get it working properly (see below) and then repeat the process with Dirsync #2.  Then, set the password on the MSOL_AD_Sync account to a known value and update the configuration on both Dirsync installations to use that password.  Piece of cake!

2)  How to get two Dirsync installations to provision only the “right” users from the source AD.

I handled this one by taking advantage of one of the few supported customizations to Dirsync:  Using an attribute to limit the objects that Dirsync will handle.  

The first part of this task is to find a way to label all users with a value that indicates what tenant they should be synchronized to.  In my case, since users are imported into MIIS from two separate SQL databases — one for each set of users — I added a flow to the SQL server MA’s to include a value which reflected the user’s origin.  For example, users arriving from the database for students from database A were labeled as belonging to A and similarly for students arriving from database B.

At this point I had MIIS set up so that every user had a label to show where they came from… but this important information was still only within MIIS.

Next, I added a flow to the MIIS management agent for AD which copies the point-of-origin value into the extensionAttribute15 attribute on the matching AD user object.  Now I had  every user in AD labeled with where they belonged.  Progress!

The last step was to configure the two Dirsync installations to each EXCLUDE the “wrong” users.  The process for doing this involves creating a “connector filter” entry within the SourceAD management agent in each Dirsync that matches the value that is applied to users that you don’t want to sync.  This is a fully supported customization for Dirsync and is documented at http://technet.microsoft.com/en-us/library/jj710171.aspx.  An example of how this looks is in the screen shot below.  In this case, any user in AD with the value “A” in extensionAttribute15 will not be processed by Dirsync.

2-dirsyncs-filterA

While doing so isn’t an absolute requirement, I strongly recommend that you make these filter modifications after Dirsync has been configured but BEFORE the first synchronization run so you don’t create a bunch of stuff in Office365 that later needs to be cleaned up.

How this looks when you turn the crank — and ongoing impact to Dirsync:

When you run Dirsync the first time, it will load all users in your AD (“listed as Adds”).  This is normal.  Then, the filter will be applied and you will see a number of objects listed as filtered disconnectors.  These are the objects that Dirsync skipped when processing them because they matched the filter.

Then just verify in your O365 tenant that the right users have been synchronized and, if necessary, check your filters if you are still seeing things that shouldn’t be there.  In my case, I had to add an extra filter condition to also exclude users with no value set in extensionAttribute15 to keep out other non-student accounts that were in AD.

NOTE:  Because Dirsync will re-check all of these disconnectors every time it runs a processing cycle, this approach will increase the load on the Dirsync server and make processing take longer.  For the customer that I was working with, with a total of about 30,000 users between the two tenants, this has not been a problem.

Advertisements

Misdirection via ADFS Config Wizard Error Dialogs

While doing the configuration of the first federation server for ADFS at a customer site, we encountered the error message below.

After spending quite a while fruitlessly testing permissions, looking at NTDS diagnostic logs, and verifying the server’s trust relationship with the AD was correct, we noticed that in the AD, the “Program Data” container was actually an OU and not a container.

My theory is that sometime in the past someone deleted the Program Data container (accidentally?) and recreated it as an OU.  After deleting the (empty) impostor OU and creating a replacement container properly, all was well.

You do not have sufficient privileges to create a container in Active directory at location CN=xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxx,CN=Microsoft,CN=Program Data,DC=xxxxxxx,DC=xxx for use with sharing certificates.  Veriy that you are logged on as a Domain Admin or have sufficient privileges to create this container, and try again.
You do not have sufficient privileges to create a container in Active Directory at location CN=xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxx,CN=Microsoft,CN=Program Data,DC=xxxxxxx,DC=xxx for use with sharing certificates. Verify that you are logged on as a Domain Admin or have sufficient privileges to create this container, and try again.

Another person who had a similar issue blogged about it here, which includes instructions on how to properly create the Program Data container.

What I’m taking away from this is an understanding that this error message appears to be thrown any time there is an exception during this step of the configuration process so you can’t take the error text at face value. :-)

Office 365 Granular License Assignment via Powershell

I’ve spent the last day or so working on how to reliably assign licenses to Office 365 for  users with Powershell.  My particular test environment is an Office 365 for Education space but the procedure here should work for any Office 365 setup.

There are a few blog posts, magazine articles and forum threads about this process but they only provided pieces of the overall puzzle when I was working this out so this is my attempt to put it all together.

Office 365 Account SKUs and SKU ID’s:

With your Office 365 subscription, you are assigned one or more account SKU’s.  These appear to be a combination of your tenant ID and a plan identifier, where the plan identifier represents a bundle of services (which we’ll get to in a bit).  To see your assigned sku’s, run the cmdlet get-msolaccountsku, which lists the ones that are available to your tenant and also some statistics about usage.

Below is sample output when I run this cmdlet against an Office 365 tenant which has educational plan A2 assigned:

PS v3.0 C:\ > Get-MsolAccountSku

AccountSkuId                       ActiveUnits WarningUnits ConsumedUnits
------------                       ----------- ------------ -------------
myschool:STANDARDWOFFPACK_STUDENT  1000        0            1
myschool:STANDARDWOFFPACK_FACULTY  100         0            0
myschool:EXCHANGESTANDARD_ALUMNI   1000        0            0
myschool:EXCHANGESTANDARD_STUDENT  1000        0            208

My tenant has four account SKU ID’s present which can be assigned to users.  Each Sku ID represents a “bundle” of one or more licensed services such as Sharepoint, Exchange, Lync, the office web apps, etc.

To see the component services of these bundles, you need to crack open its SKU ID and look at the “service plans” that it contains.  It’s easiest to do this via a two-step process.

First, you use get-msolaccountsku to get the SkuPartNumber for each of the Sku ID’s.  In all my testing, the SkuPartNumber value has been the same as the AccountSkuId minus the domain identifier prefix on it but we should assume that the two can diverge in the future.

PS v3.0 C:\ > Get-MsolAccountSku | ft accountskuid,skupartnumber

AccountSkuId                             SkuPartNumber
------------                             -------------
myschool:STANDARDWOFFPACK_STUDENT        STANDARDWOFFPACK_STUDENT
myschool:STANDARDWOFFPACK_FACULTY        STANDARDWOFFPACK_FACULTY
myschool:EXCHANGESTANDARD_ALUMNI         EXCHANGESTANDARD_ALUMNI
myschool:EXCHANGESTANDARD_STUDENT        EXCHANGESTANDARD_STUDENT

Next, let’s open up STANDARDWOFFPACK_STUDENT to see what its component services are.

Note that we’re using the SkuPartNumber here and not the AccountSkuId, which looks similar.

PS v3.0 C:\ > $plans = Get-MsolAccountSku | Where {$_.SkuPartNumber -eq "STANDARDWOFFPACK_STUDENT"}
PS v3.0 C:\ > $plans.servicestatus

ServicePlan            ProvisioningStatus
-----------            ------------------
SHAREPOINTWAC_EDU      Success
MCOSTANDARD            Success
SHAREPOINTSTANDARD_EDU Success
EXCHANGE_S_STANDARD    Success

And there we see that STANDARDWOFFPACK_STUDENT has four component services:

  • SHAREPOINTWAC_EDU: (This is for the 365 web apps, not Sharepoint)
  • MCOSTANDARD (Office 365 Lync)
  • SHAREPOINTSTANDARD_EDU (Office 365 Sharepoint)
  • EXCHANGE_S_STANDARD (Exchange)

Now, how do we use this information to apply licenses to Office 365 users with Powershell?

The primary way that you apply user licenses for O365 with Powershell is with the set-msoluserlicense cmdlet.  You can either use it on its own and specify the UPN of the targeted user on the command line or pipe a user object to it with get-msoluser.

set-msoluserlicense has three parameters that I’m focusing on for the purposes of this post:

  • -AddLicenses <string[]>
  • -LicenseOptions <LicenseOption[]>
  • -Removelicenses <string[]>

The first and last options relate to applying or removing all functions within a license “bundle” to a user and the middle one allows you to manage which services are enabled for a particular user.  In my testing, it appears that you must have an overall license “bundle” in place for a user (enabling all functions) before you can handle enabling/disabling the individual components.

The -AddLicenses and -RemoveLicenses parameters require a string value that must match one of the available AccountSkuId’s on your 365 tenant.

You can also specify licenses with the new-msoluser cmdlet via the -LicenseAssignment and -LicenseOptions parameters but I’m not going to discuss them here because they work in a similar way.

Here’s a one-liner to enable a single user for all available services within an AccountSkuId.  You do this by applying the AccountSkluID for the “bundle” that you want the user to get.

get-msoluser -UserPrincipalName first.last@myschool.edu | set-msoluserlicense -AddLicenses "myschool:STANDARDWOFFPACK_STUDENT"

And here’s how to verify that it worked by checking the ProvisioningStatus of the user’s licenses. Success == good, of course.

PS v3.0 C:\ > (get-msoluser -UserPrincipalName first.last@myschool.edu).licenses.servicestatus

ServicePlan              ProvisioningStatus
-----------              ------------------
SHAREPOINTWAC_EDU        Success
MCOSTANDARD              Success
SHAREPOINTSTANDARD_EDU   Success
EXCHANGE_S_STANDARD      Success

Disabling Specific Office 365 Functions for a User

Let’s say that my hypothetical customer isn’t ready to support Lync, so we want to shut that functionality off for our test user.  To do this, we need to create a LicenseOptions object which references the AccountSkluId for the “bundle” that the user is using for and also lists the Lync component on the DisabledPlans parameter.

First, we create a LicenseOptions object which has Lync disabled.  I’ll call it $nolync here.

PS v3.0 C:\ > $nolync = New-MsolLicenseOptions -AccountSkuId myschool:STANDARDWOFFPACK_STUDENT -DisabledPlans MCOSTANDARD

PS v3.0 C:\ > $nolync

ExtensionData     AccountSkuId                            DisabledServicePlans
-------------     ------------                            --------------------
                  Microsoft.Online.Administration.Acco... {MCOSTANDARD}

Next, we use set-msoluserlicense to apply the license options object that we just created to the user.  Everything that’s not listed in the DisabledServicePlans property will be ENABLED after applying this to the user, so “stacking” LicenseOptions calls against a user will result in a last-writer-wins scenario.

PS v3.0 C:\ > get-msoluser -UserPrincipalName first.last@myschool.edu | Set-MsolUserLicense -LicenseOptions $nolync 

PS v3.0 C:\ >

Now, we can check the servicestatus property of the user’s licenses to verify that Lync has been shut off:

PS v3.0 C:\ > (get-msoluser -UserPrincipalName first.last@myschool.edu).licenses.servicestatus 

ServicePlan            ProvisioningStatus 
-----------            ------------------ 
SHAREPOINTWAC_EDU      Success 
MCOSTANDARD            Disabled 
SHAREPOINTSTANDARD_EDU Success 
EXCHANGE_S_STANDARD    Success

IMPORTANT NOTE about SharePoint licensing with office 365.  I had a lot of trouble attempting to disable just SharePoint for users in my tenant – it failed every time and I couldn’t figure out why.  After significant head-scratching and web searches, I eventually found this forum thread, where a Microsoft support person mentions as an aside that Office Web Apps and SharePoint are co-dependent so you can’t disable SharePoint in Office 365 without also disabling the Office web apps.  While this solved my problem, the enforced linkage of the two is unfortunate as customers might want their users to have access to the office web apps (which IMO are very nice) but not SharePoint because of the support complexity that SharePoint brings.  I suspect this is because the Office web apps use Sharepoint for storage so this may be resolved when/if SkyDrive Pro arrives on the scene for 365. [ UPDATE 12/21/2012 :  This has been confirmed.  The Office web apps in Office 365 are configured to use a document library on a Sharepoint site as their storage instead of Skydrive like the free versions are. ]

For now, however, if you want to disable SharePoint for your users, make sure you also disable the Office Web Apps or you’ll get an error when you try to apply the change.

This is also a convenient example of how to disable two Office 365 functions together – just provide their identifiers as a comma-separated string to the new-msollicenseoptions cmdlet.

PS v3.0 C:\ > $nosharepoint = New-MsolLicenseOptions -AccountSkuId myschool:STANDARDWOFFPACK_STUDENT -DisabledPlans SHAREPOINTWAC_EDU,SHAREPOINTSTANDARD_EDU

PS v3.0 C:\ > $nosharepoint

ExtensionData AccountSkuId                            DisabledServicePlans
------------- ------------                            --------------------
              Microsoft.Online.Administration.Acco... {SHAREPOINTWAC_EDU, SHAREPOINTSTANDA...

PS v3.0 C:\ > get-msoluser -UserPrincipalName first.last@myschool.edu | Set-MsolUserLicense -LicenseOptions $nosharepoint

PS v3.0 C:\ > (get-msoluser -UserPrincipalName first.last@myschool.edu).licenses.servicestatus

ServicePlan             ProvisioningStatus
-----------             ------------------
SHAREPOINTWAC_EDU       Disabled
MCOSTANDARD             Success
SHAREPOINTSTANDARD_EDU  Disabled
EXCHANGE_S_STANDARD     Success

If you want to strip all licenses from a user, use the -removelicenses parameter with the AccountSkuId string for the license bundle and they’re toast.  Note that this can deprovision mailboxes, etc so use this with care!

Lastly, a few miscellaneous observations and comments about the license-handling cmdlets and objects themselves that didn’t fit in elsewhere:

  • The overall license “bundle” is passed as a STRING to the -addlicenses and -removelicenses parameters to the set-msoluserlicense cmdlet.  However, the -licenseoptions parameter to the same cmdlet requires that you pass it an OBJECT of the type LicenseOption, which is helpfully returned by the new-licenseoption cmdlet.  It would make a lot more sense if all three parameters agreed on what sort of parameter they required.
  • If you want to give a new user a granular set of permissions, it appears that you need to first assign the overall license Sku to a user (which activates all functions) and then stamp it with a licenseoptions object which has the components that you don’t want the user to see listed as disabled.  You can’t just stamp an unlicensed user with a LicenseOptions object or the assignment will fail.
  • Additionally, according to a reply to this posted request for help, the -addlicenses and -licenseoptions parameters don’t like to be used together.  This appears to be my experience as well.
  • The -UnlicensedUsersOnly parameter to get-msoluser will (obviously) return only unlicensed users – this is handy.
  • Error reporting from assignment of licenses in 365 via Powershell is TERRIBLE.  The most common cause of problems is making a typo in the new-msollicenseoptions cmdlet since it doesn’t seem to perform any input validation.

Digital Glue?

I named this blog “Digital Glue” because that describes the aspect of technology that I enjoy the most quite well – making groups of systems work together in interesting ways.  My current employer, Oxford Computer Group NA, specializes in designing and implementing complex identity management solutions using Microsoft’s Forefront Identity Manager (FIM) and related technologies.  At the moment, most of my daily work is centered on migration of educational institutions from Microsoft’s Live@EDU platform to the newer Office 365 platform.

Advisory to readers – I tend to to back and make changes to posts rather than just stick them up and leave them alone.  If the changes relate to content rather than just tweaking the phrasing of a particular item, I’ll make a note that the post has been udpated and when.

Back on the intertubes!

I’m glad to have a WordPress site back online to share some of my discoveries.  I previously worked for a major university and maintained a professional blog there where I talked about the work that I and my group did (and occasionally threw in some other stuff, like my wife’s killer recipe for rum balls).

However, with my departure now getting smaller in the rear-view mirror, that blog is sure to be de-rezzed at some point so I’m going to use this one as a similar place to post things that I find interesting.  I’m also going to try to transfer some of the more interesting things from that blog to this one because I feel they are of interest to the wider world.