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.
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.
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.
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
See my reply in the forum thread.
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
$_ 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.
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,
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?
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.
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{}
Please check the Application Log in windows. It usually gives some insight in what’s missing in the flow. It also looks like you are using the Granfeldt PS MA. It has some logging you can enable. Please see http://psma.codeplex.com/wikipage?title=Logging&referringTitle=Documentation for details on how to enable that.
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?
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;