Skip to main content

Reprocessing License Assignments in Entra ID with PowerShell

The Problem
#

Group-based licensing in Entra ID is great until it is not. License assignments get stuck in error states. A user’s license shows “Error” in the admin center. Maybe the license pool ran out temporarily and now there are available licenses, but Entra never retried. Maybe a service plan conflict caused a failure that has since been resolved. Maybe you just migrated a batch of users and the license processing stalled.

The Entra admin center lets you reprocess one user at a time. Click user, click Licenses, click Reprocess. Now do that for 200 users who all hit the same error during a bulk migration.

Enter Invoke-MgLicenseUser – the Graph PowerShell SDK cmdlet that triggers the reprocessLicenseAssignment endpoint for a user.

How It Works
#

One script, three modes:

# The nuclear option -- every licensed member in the tenant
.\Invoke-UserLicenseReprocessing.ps1

# The surgical option -- specific users from a CSV
.\Invoke-UserLicenseReprocessing.ps1 -CsvPath "C:\Reports\Users.csv"

# The single-user fix -- one person, right now
.\Invoke-UserLicenseReprocessing.ps1 -UserPrincipalName "[email protected]"

All three modes do the same thing: look up the target users, call Invoke-MgLicenseUser for each, and report results. The -TenantId parameter is available on all modes for multi-tenant environments.

Prerequisites
#

  • PowerShell 5.1+ with the Microsoft Graph PowerShell SDK
  • Microsoft.Graph.Authentication (for Connect-MgGraph)
  • Microsoft.Graph.Users (for Get-MgUser)
  • Microsoft.Graph.Users.Actions (for Invoke-MgLicenseUser)
  • Permission: User.ReadWrite.All (delegated). If running unattended via a service principal, this is an application permission that grants write access to all user objects in the tenant – scope it carefully.
  • For CSV mode: a CSV file with a UPN column

Implementation
#

All Licensed Users
#

Without parameters, the script retrieves every licensed member user and reprocesses them all:

[Array]$allUsers = Get-MgUser `
    -Filter "assignedLicenses/`$count ne 0 and userType eq 'Member'" `
    -ConsistencyLevel eventual `
    -CountVariable Records `
    -All `
    -Property Id, AssignedLicenses, UserPrincipalName |
    Sort-Object UserPrincipalName

The filter assignedLicenses/$count ne 0 and userType eq 'Member' excludes guest users and only targets users who actually have license assignments. -ConsistencyLevel eventual is required for the $count filter to work – without it, Graph returns a 400.

Specific Users from CSV
#

With -CsvPath, the script reads UPNs and looks up each user individually:

.\Invoke-UserLicenseReprocessing.ps1 -CsvPath "C:\Reports\Users.csv"

If a UPN doesn’t resolve, the script warns and moves on to the next user – it doesn’t stop. This is intentional: in a batch of 200 users, you don’t want one typo in the CSV to block the other 199.

The CSV format is just a UPN column:

Single User
#

For one-off fixes, pass the UPN directly:

.\Invoke-UserLicenseReprocessing.ps1 -UserPrincipalName "[email protected]"

If the user isn’t found, the script exits with a warning. No ambiguity, no partial processing.

The Reprocessing Loop
#

All three modes feed into the same loop:

$successCount = 0
$errorCount = 0
foreach ($user in $users) {
    try {
        Invoke-MgLicenseUser -UserId $user.Id -ErrorAction Stop | Out-Null
        Write-Host "Reprocessed: $($user.UserPrincipalName)" -ForegroundColor Green
        $successCount++
    }
    catch {
        Write-Host "Error: $($user.UserPrincipalName): $_" -ForegroundColor Red
        $errorCount++
    }
}
Write-Host "Done. $successCount succeeded, $errorCount failed." -ForegroundColor Cyan

Invoke-MgLicenseUser triggers an asynchronous reprocessing – it tells Entra to re-evaluate the user’s license state. The actual processing happens in the background. Don’t expect instant results; give it a few minutes.

When to Use Which Mode
#

All users – after a licensing SKU change (E3 to E5 migration), a known outage that caused widespread failures, or a major group membership restructure.

CSV – after a bulk user migration where specific users hit errors, or when you have a support ticket with a list of affected users.

Single user – when one person’s license is stuck and you just need to fix it now.

Verifying Results
#

After reprocessing, give it a few minutes and then check whether the errors cleared:

Get-MgUser -UserId "[email protected]" -Property licenseAssignmentStates |
    Select-Object -ExpandProperty LicenseAssignmentStates |
    Select-Object AssignedByGroup, State, Error

State should show Active and Error should be empty. If errors persist, the underlying issue hasn’t been resolved – a service plan conflict, insufficient licenses, or a misconfigured group assignment.

Security Considerations
#

  • User.ReadWrite.All is a powerful permission. Invoke-MgLicenseUser doesn’t modify licenses directly – it triggers reprocessing – but the permission scope allows writing to user objects broadly. In an application context (service principal), this grants tenant-wide user write access. Be deliberate about who gets this.
  • The “all users” mode processes every licensed user in the tenant. In a large tenant, that’s thousands of API calls. The Graph SDK handles 429 throttling automatically by respecting the Retry-After header – you don’t need to add Start-Sleep calls.
  • Each reprocessLicenseAssignment call is idempotent. Running it on a user whose licenses are already healthy does nothing – it re-evaluates and finds nothing to fix. Harmless.

When to Use This / When Not To
#

Use this when license assignments are stuck in error state, after license SKU changes, after resolving service plan conflicts, or after restoring license availability.

Don’t use this as a routine task. If you find yourself running this regularly, the problem is not licenses – it’s your licensing model. Figure out what keeps breaking. And reprocessLicenseAssignment does not fix conflicts caused by incompatible service plans – you need to resolve the conflict first, then reprocess.

GitHub
#

Invoke-UserLicenseReprocessing.ps1