When Microsoft Swapped Our E5 License — and How We Fixed It with PowerShell
One day, our team noticed that the Office 365 E5 license had completely disappeared from the Microsoft 365 Admin Center. In its place was a new license called Microsoft 365 E5. Microsoft had retired the old SKU and replaced it with a new one — without any migration happening automatically on the group-based licensing side.
The Situation
On the surface, everything seemed fine. Users who already had the old license were still able to use all their Office apps and services — Exchange, Teams, SharePoint — without any interruption. This is because Microsoft preserves active assignments for existing users even after a SKU transition.
New users added to existing groups were silently failing to receive their licenses. No error was shown to the end user. From a helpdesk perspective, tickets started coming in: "I can't access my mailbox", "Teams isn't working" — all from recently onboarded accounts.
Why Did This Happen?
In Microsoft 365, when you assign a license to a group (group-based licensing), it stores the SkuId — a unique GUID that identifies the specific product. When Microsoft retired the old SKU and introduced the new one, the SkuId changed. The groups in our tenant were still configured with the old SkuId, so any new license processing by those groups simply failed silently. Existing users retained their licenses as a snapshot — but the group could no longer assign new ones.
| SKU Part Number | Product Name | Status |
|---|---|---|
| ENTERPRISEPREMIUM | Office 365 E5 | Retired / Expired |
| SPE_E5 | Microsoft 365 E5 | Active (New) |
To make it more complex, each group had its own set of disabled service plans — meaning some groups had certain features like Microsoft Purview or Customer Lockbox turned off intentionally. We couldn't just blindly assign the new SKU; we had to preserve that per-group configuration too.
SkuPartNumber or a
specific service plan GUID in your tenant.
How We Solved It
The fix required three things: find all affected groups, capture their existing service plan configuration, and then reassign the new SKU with the same settings. We did this entirely using Microsoft Graph PowerShell.
Step 1 — Connect and Identify Both SKUs
Connect to Microsoft Graph with all required scopes upfront, then confirm the old and
new SkuIds by querying the exact SkuPartNumber values. Copy the
SkuId GUID values from the output — you will need them in the steps below.
# Connect with all required scopes upfront Connect-MgGraph -Scopes "Directory.Read.All", "Organization.Read.All", "Group.ReadWrite.All" # Retrieve both the old and new SKU by exact SkuPartNumber Get-MgSubscribedSku -All | Where-Object { ($_.SkuPartNumber -eq "ENTERPRISEPREMIUM") -or ($_.SkuPartNumber -eq "SPE_E5") } | Select-Object SkuPartNumber, SkuId, ConsumedUnits
Step 2 — Find All Groups Still Assigned the Old SKU
Using the old SkuId, query all groups that still have it assigned. Because
AssignedLicenses is a collection property — not a scalar — you must use
the OData any() lambda operator to filter against the skuId
sub-property. A plain eq comparison will throw a BadRequest error.
$OldSkuId = "<ENTERPRISEPREMIUM-sku-guid>" # paste from Step 1 output Get-MgGroup -Filter "assignedLicenses/any(s:s/skuId eq $($OldSkuId))" ` -All -ConsistencyLevel eventual -CountVariable c ` -Property Id, DisplayName, AssignedLicenses | Select-Object Id, DisplayName
-ConsistencyLevel eventual and
-CountVariable parameters are mandatory when using advanced OData filters
like any() against Microsoft Graph. Omitting them will result in a
BadRequest error.
Step 3 — Export Groups with Their Service Plan Configuration
For each group, capture the service plan names and whether each plan is Enabled or Disabled. This gives you a full audit record before making any changes — critical for verifying the migration and reconstructing configurations if needed.
$OldSkuId = "<ENTERPRISEPREMIUM-sku-guid>" # Get SKU metadata once — contains all service plans in this SKU $Sku = Get-MgSubscribedSku -All | Where-Object { $_.SkuId -eq $OldSkuId } $Report = [System.Collections.Generic.List[PSObject]]::new() $Groups = Get-MgGroup -Filter "assignedLicenses/any(s:s/skuId eq $($OldSkuId))" ` -All -ConsistencyLevel eventual -CountVariable c ` -Property Id, DisplayName, AssignedLicenses foreach ($Group in $Groups) { $License = $Group.AssignedLicenses | Where-Object { $_.SkuId -eq $OldSkuId } $DisabledGuids = $License.DisabledPlans foreach ($Plan in $Sku.ServicePlans) { $Status = if ($Plan.ServicePlanId -in $DisabledGuids) { "Disabled" } else { "Enabled" } $Report.Add([PSCustomObject] @{ GroupName = $Group.DisplayName GroupId = $Group.Id ServicePlanName = $Plan.ServicePlanName ServicePlanId = $Plan.ServicePlanId Status = $Status }) } } $Report | Export-Csv -Path "C:\Temp\Group_ServicePlan_Audit.csv" -NoTypeInformation Write-Host "Exported to C:\Temp\Group_ServicePlan_Audit.csv" -ForegroundColor Green
Step 4 — Reassign the New SKU, Preserving Disabled Plans
After reviewing the exported CSV, loop through each group and assign the new
SPE_E5 SkuId while carrying over the exact same disabled service plan
GUIDs from the old assignment. This ensures no accidental feature enablement across
any group.
$OldSkuId = "<ENTERPRISEPREMIUM-sku-guid>" $NewSkuId = "<SPE_E5-sku-guid>" # paste from Step 1 output $Groups = Get-MgGroup -Filter "assignedLicenses/any(s:s/skuId eq $($OldSkuId))" ` -All -ConsistencyLevel eventual -CountVariable c ` -Property Id, DisplayName, AssignedLicenses foreach ($Group in $Groups) { $OldLicense = $Group.AssignedLicenses | Where-Object { $_.SkuId -eq $OldSkuId } $DisabledPlans = $OldLicense.DisabledPlans # carry over existing disabled plans Write-Host "Assigning new SKU to: $($Group.DisplayName)" -ForegroundColor Cyan Set-MgGroupLicense -GroupId $Group.Id ` -AddLicenses @{ SkuId = $NewSkuId DisabledPlans = $DisabledPlans } ` -RemoveLicenses @() Write-Host " Done. Disabled plans preserved." -ForegroundColor Green }
LicenseAssignmentStates on individual users or
LicenseProcessingState on the group object.
The Outcome
After running the migration script, the new Microsoft 365 E5 SKU was assigned to all
previously affected groups. The ConsumedUnits counter on the new SKU began
incrementing as group-based licensing processed the assignments. New users added to those
groups after this point received their licenses correctly.
Importantly, all service plan configurations — the intentional disablements per group — were preserved exactly as they were under the old SKU. No accidental feature enablement. No support tickets from users getting unexpected access.
Key Takeaways
- When Microsoft retires a SKU, group-based licensing does not migrate automatically. Groups retain the old SkuId and silently stop working for new assignments while existing users remain unaffected.
- The failure is invisible until a newly onboarded user reports missing access — it does not surface at the time of the SKU change.
- Always export and preserve the disabled service plans per group before reassigning. Skipping this step loses per-group entitlement configurations.
- Use the OData
any()lambda filter with-ConsistencyLevel eventualwhen querying groups byassignedLicenses.skuId. A plaineqfilter returns a BadRequest error. - A regular audit of your tenant's licensed groups against active subscriptions is a good habit to prevent this from going unnoticed again.