Bulk Migrate Office 365 Licenses

This is a follow-up post to Bulk Enable Office 365 License Options and reflects an unusually complex scenario where O365 licenses must be migrated to a different SKU.  Here is a real-world example…

Customer initially purchases E1 licenses and begins to provision and migrate accounts using the included service options (i.e. Exchange, SharePoint, and/or Lync).  Customer then realizes they require an expanded feature set and decides to purchases E3 licenses to replace the E1.  However, by this time many users already have a live Exchange mailbox under E1.  To complicate matters further, some users have now been enabled for SharePoint and/or Lync using the new E3 license while Exchange is still enabled under the original E1.  (Yes, this is possible as long as the same service is not enabled in both SKUs.)  Wow, what a mess!  So, how do we “flip” all the E1 licenses to E3 without data loss?  I’m glad you asked :-)

First, a couple key considerations:  (1) un-assigning an Exchange license for users which have already been enabled can result in the re-provisioning of an empty mailbox; (2) at no point can the same service be enabled under two different SKUs; (3) if Office Web Apps is to enabled under E3 then SharePoint must also be assigned under E3.  Scared yet?  Here we go…

We will definitely need some logging for this:

$LogFile = "c:\o365\SwitchLicense.log"; Get-Date | Out-File $LogFile

We already know that if the user does not have a mailbox under E1, we will not be enabling the Exchange option under E3.  So, let’s define that license option:

$NoExchange = New-MsolLicenseOptions -AccountSkuId "tenant:ENTERPRISEPACK" -DisabledPlans "EXCHANGE_S_ENTERPRISE"

Next, we’ll create a data set to run against.  As always, I prefer to use a CSV import when making bulk changes.  But, the task could be run against all accounts.  In either case, keep in mind that some users may have both E1 and E3 assigned.  So, we must query for each.  If the user is not licensed at all, then we’ll make note of that in the log file and move on:

Import-Csv "c:\o365\ImportFile.csv" | ForEach {

#Get-MsolUser -All | ForEach {

$Upn = $_.UserPrincipalName; $Licensed = $_.IsLicensed

$UserLicense0 = (Get-MsolUser -User $Upn).Licenses[0]

$UserLicense1 = (Get-MsolUser -User $Upn).Licenses[1]

If ($Licensed -eq $False) { "SKIP: No license currently assigned [$Upn]" | Out-File $LogFile -Append }

If the user is currently licensed for only E3, then there is nothing to “flip”.  We’ll make note of that in the log and continue:

If ($UserLicense0.AccountSkuId -eq "tenant:ENTERPRISEPACK" -and $UserLicense1 -eq $null)

{ "SKIP: Only E3 license currently assigned [$Upn]" | Out-File $LogFile -Append }

If the user is only licensed for E1, then we can easily flip to E3.  However, we don’t necessarily want to enable options under E3 that were not enabled under E1.  For now, let’s assume all options under E1 are disabled.  We will query the assigned E1 license to determine which options are actually enabled and write the result to the log file:

If ($UserLicense0.AccountSkuId -eq "tenant:STANDARDPACK" -and $UserLicense1 -eq $null) {

$Lync = "Disabled"; SharePoint = "Disabled"; $Exchange = "Disabled"

(Get-MsolUser -User $Upn).Licenses[0].ServiceStatus | ForEach {

If ($_.ServicePlan.ServiceName -eq "MCOSTANDARD" -and $_.ProvisioningStatus -ne "Disabled") { $Lync = "Enabled" }

If ($_.ServicePlan.ServiceName -eq "SHAREPOINTSTANDARD" -and $_.ProvisioningStatus -ne "Disabled") { $SharePoint = "Enabled" }

If ($_.ServicePlan.ServiceName -eq "EXCHANGE_S_STANDARD" -and $_.ProvisioningStatus -ne "Disabled") { $Exchange = "Enabled" } }

"SCAN: Only E1 license currently assigned [$Upn] Lync:$Lync, SharePoint:$SharePoint, Exchange:$Exchange" | Out-File $LogFile -Append

If the E1 user does not have Exchange assigned, we can safely flip to E3 with Exchange disabled.  However, if the E1 user does have Exchange assigned, then we must transition to E3 by removing the old license and assigning the new license in a single command.  In both cases, we will write the results to the log file:

If ($Exchange -eq "Disabled") { Set-MsolUserLicense -User $Upn -RemoveLicenses "tenant:STANDARDPACK" -AddLicenses "tenant:ENTERPRISEPACK" -LicenseOptions $NoExchange }

Else { Set-MsolUserLicense -User $Upn -RemoveLicenses "tenant:STANDARDPACK" -AddLicenses "tenant:ENTERPRISEPACK" }

"MOVE: E1 license moved to E3 [$Upn] Office:Enabled, Lync:Enabled, SharePoint:Enabled, WebApps:Enabled, Exchange:$Exchange" | Out-File $LogFile -Append }

This is where it gets tricky.  If the user has both E1 and E3 assigned, we must determine under which license Exchange is enabled.  Once again, we’ll assume all options are disabled, query both licenses to identify which options are actually enabled, and write the results to the log file:

If ($UserLicense0.AccountSkuId -eq "tenant:ENTERPRISEPACK" -and $UserLicense1.AccountSkuId -eq "tenant:STANDARDPACK") {

$Office = "Disabled"; $Lync = "Disabled"; $WebApps = "Disabled"; $SharePoint = "Disabled"; $Exchange0 = "Disabled"

(Get-MsolUser -User $Upn).Licenses[0].ServiceStatus | ForEach {

If ($_.ServicePlan.ServiceName -eq "OFFICESUBSCRIPTION" -and $_.ProvisioningStatus -ne "Disabled") { $Office = "Enabled" }

If ($_.ServicePlan.ServiceName -eq "MCOSTANDARD" -and $_.ProvisioningStatus -ne "Disabled") { $Lync = "Enabled" }

If ($_.ServicePlan.ServiceName -eq "SHAREPOINTWAC" -and $_.ProvisioningStatus -ne "Disabled") { $WebApps = "Enabled" }

If ($_.ServicePlan.ServiceName -eq "SHAREPOINTENTERPRISE" -and $_.ProvisioningStatus -ne "Disabled") { $SharePoint = "Enabled" }

If ($_.ServicePlan.ServiceName -eq "EXCHANGE_S_ENTERPRISE" -and $_.ProvisioningStatus -ne "Disabled") { $Exchange0 = "Enabled" } }

"SCAN: E3 license currently split [$Upn] Office:$Office, Lync:$Lync, WebApps:$WebApps, SharePoint:$SharePoint, Exchange:$Exchange0" | Out-File $LogFile -Append   

$Lync = "Disabled"; $SharePoint = "Disabled"; $Exchange1 = "Disabled"

(Get-MsolUser -User $Upn).Licenses[1].ServiceStatus | ForEach {

If ($_.ServicePlan.ServiceName -eq "MCOSTANDARD" -and $_.ProvisioningStatus -ne "Disabled") { $Lync = "Enabled" }

If ($_.ServicePlan.ServiceName -eq "SHAREPOINTSTANDARD" -and $_.ProvisioningStatus -ne "Disabled") { $SharePoint = "Enabled" }

If ($_.ServicePlan.ServiceName -eq "EXCHANGE_S_STANDARD" -and $_.ProvisioningStatus -ne "Disabled") { $Exchange1 = "Enabled" } }

"SCAN: E1 license currently split [$Upn] Lync:$Lync, SharePoint:$SharePoint, Exchange:$Exchange1" | Out-File $LogFile –Append

We’re getting close.  If Exchange is enabled under E3 in a “split license” scenario, we can safely remove E1 and enable any previously assigned options under E3.  Remember, Exchange is already enabled under E3:

If ($Exchange0 -eq "Enabled" -and $Exchange1 -eq "Disabled") {

$Exchange = "Enabled"

Set-MsolUserLicense -User $Upn -RemoveLicenses "tenant:STANDARDPACK"

Set-MsolUserLicense -User $Upn -AddLicenses "tenant:ENTERPRISEPACK" }

However, if Exchange is enabled under E1 we must first remove E3 and then flip the entire E1 license (Exchange included) in a single command:

If ($Exchange0 -eq "Disabled" -and $Exchange1 -eq "Enabled") {

$Exchange = "Enabled"

Set-MsolUserLicense -User $Upn -RemoveLicenses "tenant:ENTERPRISEPACK"

Set-MsolUserLicense -User $Upn -RemoveLicenses "tenant:STANDARDPACK" -AddLicenses "tenant:ENTERPRISEPACK }

The last possible scenario would be that Exchange is not enabled under either license.  So, we will simply remove both licenses and reassign E3 without Exchange:

If ($Exchange0 -eq "Disabled" -and $Exchange1 -eq "Disabled") {

$Exchange = "Disabled"

Set-MsolUserLicense -User $Upn -RemoveLicenses "tenant:ENTERPRISEPACK"

Set-MsolUserLicense -User $Upn -RemoveLicenses "tenant:STANDARDPACK"

Set-MsolUserLicense -UserPrincipalName $Upn -AddLicenses "tenant:ENTERPRISEPACK" -LicenseOptions $NoExchange }

Of course, we will write the results of all this license manipulation to the log file:

"JOIN: E1 license merged into E3 [$Upn] Office:Enabled, Lync:Enabled, SharePoint:Enabled, WebApps:Enabled, Exchange:$Exchange" | Out-File $LogFile –Append } }

Problem solved!  This scenario reflects an incredibly complex scenario that I hope you won’t encounter.  But, if you find yourself facing similar circumstance, I trust some of these concepts will prove useful.  It obviously goes without saying that you must thoroughly test this solution on sample accounts or a test tenant before running it against your production environment.  RjZ

BulkMigrateLicenses.zip