Managing Office 365 licenses using FIM 2010

When starting to use Office 365 in large scale you soon realize that although DirSync will solve most of your synchronization needs it will not solve the problem of assigning the correct license to different users. In this post I will show you how I solved this problem using FIM 2010 with a PowerShell MA.

The problem at hand was not only to license unlicensed users but to manage licenses in the respect of changing the license and removing the license based on information in FIM.

Background information

The customer has almost 60,000 users in FIM. In that we have 900 staff and a little over 57,000 students.

In FIM we use the employeeType attribute to decide the type of user. In this context we have three different values to consider, Staff, Student and Alumni. Each type should have a different license assigned to them. And since typically a Student will eventually become an Alumni we need to manage that transformation as well.

In FIM we have also created a simple boolean attribute called off365Licensed. This attribute is the main “switch” to decide if you should have a license at all.

Office 365 Licenses

Different subscriptions in Office 365 gives you different licenses to work with so keep in mind that this post only shows one type of subscription. The EDU subscription gives you a couple of “free” licenses to use but in order to use them we need to learn how they look since we actually will need to disable parts of some licenses in most cases.

As an example I will show you the Faculty licenses that are “free”.
You have the STANDARDWOFFPACK_FACULTY and also the PROJECTONLINE_PLAN_1_FACULTY.

But they consist of different parts.
The STANDARDWOFFPACK_FACULTY has four parts in it

  • SHAREPOINTWAC_EDU
  • MCOSTANDARD
  • SHAREPOINTSTANDARD_EDU
  • EXCHANGE_S_STANDARD

The PROJECTONLINE_PLAN_1_FACULTY has three parts in it

  • SHAREPOINT_PROJECT_EDU
  • SHAREPOINTWAC_EDU
  • SHAREPOINTENTERPRISE_EDU

The problem here is that they overlap and both contain SharePoint licenses that cannot be assigned at the same time. So in our scripts we need to disable parts of the licenses that we cannot assign if we also want to assign the other.

PowerShell MA

We decided to use the PowerShell MA developed by Sören Granfeldt.
I do not do Provisioning and De-provisioning in this case, that part I leave to DirSync. In this MA we import the users, join them to the corresponding user in the MetaVerse and then make sure the correct license is assigned.

Below I show you some parts of the scripts involved but please download O365MAScripts to get the complete scripts.

Schema

The Schema script in this case is quite simple. I used the UPN as anchor in this case.

$obj = New-Object -Type PSCustomObject
$obj | Add-Member -Type NoteProperty -Name "Anchor-UPN|String" -Value 1
$obj | Add-Member -Type NoteProperty -Name "objectClass|String" -Value "user"
$Obj | Add-Member -Type NoteProperty -Name 'IsLicensed|Boolean' -Value $true
$Obj | Add-Member -Type NoteProperty -Name 'LicenseType|String' -Value "Staff"
$obj

Import Script

The import script imports the current MsolUsers

$Users = Get-MsolUser -MaxResults 50000 -DomainName company.com

but also checks the current license assigned

if($user.IsLicensed)
{
if($User.Licenses.AccountSkuId[0].EndsWith("FACULTY"))
{$obj.Add("LicenseType", "Staff")}
elseif($User.Licenses.AccountSkuId[0].EndsWith("STUDENT"))
{$obj.Add("LicenseType", "Student")}
elseif($User.Licenses.AccountSkuId[0].EndsWith("ALUMNI"))
{$obj.Add("LicenseType", "Alumni")}
else {$obj.Add("LicenseType", "Unknown")}
}

Export

The export script picks up the users from O365

$msoluser=Get-MsolUser -UserPrincipalName $_.DN
$IsLicensed=$msoluser.IsLicensed

I do not bother to look at the current license of the users since changing licenses involves removing the old license and adding the new license. My script just removes the old licenses and assigns the correct ones.
Removing old licenses can be done in a single line

Set-MsolUserLicense -UserPrincipalName $msolUser.UserPrincipalName -RemoveLicenses $msoluser.Licenses.AccountSkuId

Adding the correct license involves defining the licenses including the excludes discussed earlier in this post.
Please note that before we can assign any licenses we need to set the UsageLocation on the user.

if(!$msoluser.UsageLocation)
{Set-MsolUser -UserPrincipalName $msolUser.UserPrincipalName -UsageLocation $DefaultUsageLocation;}

We then assign the license depending on the employeeType in FIM.

if($LicenseType -eq "Staff")
{
Set-MsolUserLicense -UserPrincipalName $msolUser.UserPrincipalName -AddLicenses $STANDARDWOFFPACK_FACULTY -LicenseOptions $FacultyStnd;
Set-MsolUserLicense -UserPrincipalName $msolUser.UserPrincipalName -AddLicenses $PROJECTONLINE_PLAN_1_FACULTY;
}
elseif($LicenseType -eq "Student")
{
Set-MsolUserLicense -UserPrincipalName $msolUser.UserPrincipalName -AddLicenses $STANDARDWOFFPACK_STUDENT -LicenseOptions $StudStnd;
Set-MsolUserLicense -UserPrincipalName $msolUser.UserPrincipalName -AddLicenses $PROJECTONLINE_PLAN_1_STUDENT;
}
elseif($LicenseType -eq "Alumni")
{
Set-MsolUserLicense -UserPrincipalName $msolUser.UserPrincipalName -AddLicenses $EXCHANGESTANDARD_ALUMNI;
}
else
{
throw "Unknown LicenseType"
}

Synchronization Rule

Finally we need to configure the Outbound Synchronization rule. We need two flows and I need a condition on the LicenseType to only set in on Licensed users.

O365 MA Outbound Synchronization Rule

Office 365 Management Agent Outbound Synchronization Rule

Conclusion

In this post I have showed you one example on how to manage Office 365 licenses. As you can imagine this task can grow substantially if we also were to manage individually selectable licenses in some way. We have discussed this at this particular client, using the FIM portal for self-service to add additional licenses. That would off course require some major changes to current license management.

12 Replies to “Managing Office 365 licenses using FIM 2010”

  1. David Ogborne

    Hi Kent,

    This looks awesome and thanks for sharing it. I’m not a FIM expert so i’m struggling with configuring FIM and I don’t have the FIM portal. Would you be able to export your MA and share it with me or provide some instructions on how to configure FIM through the Synchronization Service Manager.

    Thanks,
    David.

    Reply
  2. jmanley501

    Hi Kent,
    Have you used the PowerShell MA to connect to Exchange Online (Office 365) To manage exchance type items in 365 for example Archive and Legal Hold? I’m attempting to do this and I get an error when I hit the Import-PSSession line to get the remote powershell session imported.
    Wasn’t sure if anyone else was even trying this. The script runs fine when I run it on it’s own.
    I’ve posted on the forums, I hope maybe you can take a look if you have some time.
    Thanks;
    Jonathan

    http://social.technet.microsoft.com/Forums/en-US/74d19cca-3a07-477a-b8f6-28d5f1046c48/granfeldt-powershell-ma-importpssession?forum=ilm2#0ac1b762-bb38-493d-bdc7-ecbf5de3b3c8

    Reply
  3. Yannick27

    Hi Kent,
    Thanks for sharing!
    I’m trying to do something similar with Exchange Online but i don’t understand from where comes the $_.DN or $_.Identifier in your export script. Is there an inbound flow rule am missing?
    Cheers,
    Yannick

    Reply
    • Kent Post author

      $_ is the object piped from the sync engine to the script. .DN and .Identifier are attributes on these objects that do not require attribute flows. It’s part of all exported objects.

      Reply
  4. aj

    I am uclear how the “islicensed” is set? Is it toggled manually in the portal. Or do you set it via one of the MA’s in the Synch server? I need to set the licensing based on employeestatus and type ( eg. “Active” & “Fulltime”) and wonder if I set that in a HRIS MA Rules Extension,

    Reply
  5. aj

    Repeating question with better diction – hopefully?
    I am uclear how your solution is setting the “islicensed” value?
    – Is it toggled manually in the portal.
    – Or do you set it via one of other MA’s in the Synch server?
    I need to set my licensing based on employeestatus and type ( eg. “Active” & “Fulltime”) and wonder if setting that in a HRIS MA Rules Extension, then flowing through to the 0365PSMA would provide a reasonable solution?

    Reply
    • Kent Post author

      The IsLicensed boolean value I use in my example can be set in many ways and also differ depending on type of user. If you are using the FIMService, you could use a Set with all Active & Fulltime & Employee and use a small Workflow to set it to True on Transition In and to False on Transition Out. In a Syncrule or in Rules Extension, if you don’t have the FIMService, you can also easily set it, as you say, based on HR MA.

      Reply
  6. aj

    I hope you can assist me. I am trying to implement this licensing step in my environment. I can not seem to get the export process to work. I have tried both with and without the Begin/process/End structure. Unfortunately I cannot seem to get my exports to run without an “ma-extension-error” on each pending export./ I will include my export script
    >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    $VerbosePreference = “Continue”
    param
    (
    $Username,
    $Password,
    $Creds,
    $ExportType
    )

    #Begin{
    Import-Module MSOnline -Force
    $SecurePassword = ConvertTo-SecureString $Password -AsPlainText -Force
    $Creds = New-Object System.Management.Automation.PSCredential $Username, $SecurePassword
    Connect-MsolService -Credential $Creds
    $DebugFilePath = “C:\PSMA\exportdebug.txt”
    #Some default License values
    $DefaultUsageLocation = “US”
    #COP license
    $O365STANDARD_ENTGOV = “cityofphoenix:ENTERPRISEPACK_GOV”
    if(!(Test-Path $DebugFilePath))
    {$DebugFile = New-Item -Path $DebugFilePath -ItemType File}
    else
    {$DebugFile = Get-Item -Path $DebugFilePath}

    ” ———–” | Out-File -Append $DebugFile
    “Starting export : ” + (Get-Date) | Out-File $DebugFile -Append
    #}
    #Process{
    #Initialize Parameters
    $Identifier = $_.Identifier
    $objectGuid = $_.DN
    $ErrorName = “success”
    $ErrorDetail = $null
    $date = Get-Date -Format “yyyy-MM-dd”

    $Action = $_.ObjectModificationType
    $Action | Out-File -Append $DebugFile
    [bool] $IsLicensed = $_.AttributeChanges | where {$_.Name -eq “IsLicensed”} | foreach { [bool] $_.ValueChanges[0].Value }

    $AlreadyLicensed = [bool] (Get-MsolUser -userprincipalname $user -ErrorAction SilentlyContinue | Select -Expand isLicensed)
    “Should License: $IsLicensed” | Out-File -Append $DebugFile
    “Is Licensed: $AlreadyLicensed” | Out-File -Append $DebugFile

    #Verify changetype ( you cant add or delete, only modify)
    if ($Action -ieq ‘add’)
    {
    if ($IsLicensed)
    {
    if (!$AlreadyLicensed)
    {
    “Add: Adding license to $User” | Out-File -Append $DebugFile
    # Set-MsOlUser -UserPrincipalName $User -UsageLocation “DK”
    # Set-MsolUserLicense -UserPrincipalName $User -AddLicenses $License -ErrorVariable err -WarningVariable warn
    }
    else
    {
    “Add: $User already licensed” | Out-File -Append $DebugFile
    }
    }

    }

    if ($Action -ieq ‘delete’)
    {
    # deletes are caught by importing deleted objects from Azure AD
    write-output “Delete user not supported” | Out-File $DebugFile -Append
    }

    #Supported ChangeType is Replace
    if ($Action -ieq “replace”)
    {
    if ($IsLicensed)
    {
    if (!$AlreadyLicensed)
    {
    “Replace: Adding license to $User” | Out-File -Append $DebugFile
    # Set-MsOlUser -UserPrincipalName $User -UsageLocation “DK”
    # Set-MsolUserLicense -UserPrincipalName $User -AddLicenses $License -ErrorVariable err -WarningVariable warn
    }
    else
    {
    “Replace: $User already licensed” | Out-File -Append $DebugFile
    }
    }
    else
    {
    if ($AlreadyLicensed)
    {
    “Replace: Removing license from $User” | Out-File -Append $DebugFile
    # Remove-MsolUser -UserPrincipalName $User -Force -ErrorVariable err -WarningVariable warn
    }
    }
    }
    #}
    #end{}

    Reply
  7. Björn Hogeman

    Nowhere is de value of -LicenseOptions populated in the script.

    My problem:

    $options = New-MsolLicenseOptions -AccountSkuId stichtingfiorettiteylingen:ENTERPRISEPACK_FACULTY -DisabledPlans $null
    $status = Set-MsolUserLicense -UserPrincipalName $o365users[$Position].UserPrincipalName -LicenseOptions $options -ErrorAction:SilentlyContinue

    I should think this enables all services because DisabledPlans is $null so there should be no disabled plans.

    But if I look (Get-MsolUser -UserPrincipalName SomeUPNfromAUser).licenses[0].servicestatus | ft -AutoSize

    ServicePlan ProvisioningStatus
    ———– ——————
    OFFICE_FORMS_PLAN_2 Success
    PROJECTWORKMANAGEMENT Success
    SWAY Disabled
    INTUNE_O365 PendingActivation
    YAMMER_EDU Success
    RMS_S_ENTERPRISE Success
    OFFICESUBSCRIPTION Success
    MCOSTANDARD Success
    SHAREPOINTWAC_EDU Disabled
    SHAREPOINTENTERPRISE_EDU Disabled
    EXCHANGE_S_ENTERPRISE Success

    There are 3 services disabled. In the GUI I can enable this but how to do this with powershell for all users?

    Reply
    • Kent Post author

      You need to add the disabled plans to the license like in the snippets below.
      $StndDisabledPlans= @()
      $StndDisabledPlans +=”SHAREPOINTSTANDARD_EDU”
      $StndDisabledPlans +=”SHAREPOINTWAC_EDU”

      $StudStnd = New-MsolLicenseOptions -AccountSkuId myschool:STANDARDWOFFPACK_STUDENT -DisabledPlans $StndDisabledPlans;

      Reply

Leave a Reply to Yannick27 Cancel reply

Your email address will not be published. Required fields are marked *