License Assignments Can Encounter Glitches

A recent discussion about creating Entra ID accounts with the Microsoft Graph PowerShell SDK asked if it is possible to create an account and assign a license in the same command. I couldn’t find a way to do this and asked Microsoft. They confirmed that the New-MgUser cmdlet cannot assign a license when creating a new account.

Thinking about the situation, it’s probably best to confirm that a new account exists before proceeding to assign licenses to individual accounts or to a batch of accounts using bulk assignments. Error handling will be easier for a start.

Assigning Multiple Licenses at One Time

Another discussion is whether to assign multiple licenses at one time or to assign licenses individually. For example, you can assign several product licenses to an account by constructing an array of license assignments and using the array as input to the Set-MgUserLicense cmdlet.

To show what I mean, this code creates an array containing the SKU identifiers for the Viva, SharePoint-Syntax advanced management, and Office 365 E3 product licenses:

$DepartmentSkus = @(
   @{SkuId = '61902246-d7cb-453e-85cd-53ee28eec138'},
   @{SkuId = '6ee9b90c-0a7a-46c4-bc96-6698aa3bf8d2'},
   @{SkuId = '6fd2c87f-b296-42f0-b197-1e91e994b900'}
)

Assigning the set of licenses in a single operation is done with:

Set-MgUserLicense -UserId $UserId -AddLicenses $DepartmentSKUs -RemoveLicenses @()

This approach is effective if everything goes smoothly and all the specified licenses are available. Again, error handling is more difficult if things go wrong.

Checking if Accounts Already Have Licenses

Speaking of error conditions, two obvious problems come to mind:

  • The user account already has an assignment for a product license.
  • The tenant has no further licenses for a product available for assignment.

It’s easy to build checks for these conditions. Let’s explore how.

Let’s assume we want to assign a set of licenses to accounts, which we’ve already loaded into an array. The SKU identifiers for the licenses are defined as follows:

[array]$DesiredSKUs = '6fd2c87f-b296-42f0-b197-1e91e994b900', 'f30db892-07e9-47e9-837c-80727f46fd3d', '1f2f344a-700d-42c9-9427-5cea1d5d7ba6', '6ee9b90c-0a7a-46c4-bc96-6698aa3bf8d2'

To check if a user account already has any of these licenses, we first use the Get-MgUserLicenseDetail cmdlet to retrieve the licenses held by the account and then check each license in the set against the user’s licenses. For example:

[array]$AdjustedSKUs = $Null
[array]$CurrentLicenses = Get-MgUserLicenseDetail -UserId $User | Select-Object -ExpandProperty SkuId
ForEach ($Sku in $DesiredSkus) {
    If ($Sku -in $CurrentLicenses) {
      Write-Host ("SKU {0} is already assigned to {1} so its assignment will be ignored" -f $Sku, $User) -ForegroundColor DarkRed
      $LicenseFound = $True       
    } Else {
      $AdjustedSKUs += $Sku
    }
} # End Foreach SKU1

If the user doesn’t already have a license, the code adds to the $AdjustedSKUs array. This array then serves as the input to our second check to see if licenses are available for assignment.

Checking License Availability

To know what licenses are available, the script fetches the set of product subscriptions known to the tenant and builds a hash table with keys for the SKU identifiers and values for the number of available licenses:

[array]$Skus = Get-MgSubscribedSku

$AvailableLicenses = @{}
ForEach ($S in $Skus) {
  $AvailableUnits = ($S.PrepaidUnits.Enabled - $S.ConsumedUnits)
  $AvailableLicenses.Add([string]$S.SkuId, $AvailableUnits)
}

The hash table looks like this:

Name                           Value
----                           -----
26d45bd9-adf1-46cd-a9e1-51e9a… 2
a403ebcc-fae0-4ca2-8c8c-7a907… 999995
6ee9b90c-0a7a-46c4-bc96-6698a… 0
b05e124f-c7cc-45a0-a6aa-8cf78… -2

SKUs with minus values are probably due to expired subscriptions where user accounts still have assigned licenses. However, those licenses no longer work because they’re expired. Our code can use the hash table to check if licenses are available for each of the products we want to assign to users:

ForEach ($Sku in $AdjustedSkus) {
     If ($AvailableLicenses[$Sku] -gt 0) {
        $SkusToAssign += $Sku
     } Else {
       Write-Host ("No licenses are available to assign SKU {0} to user {1}" -f $Sku, $User) -ForegroundColor Red
     }
} # End ForEach SKU

License Assignment with Confidence

The output from the check for available licenses is an array holding the set of SKU identifiers that the script can assign. We do this by calling Set-MgUserLicense to process each assignment. To prevent other attempts to assign licenses from failing due to unavailability, the script includes code to decrement the number of available licenses noted in the hash table after a successful assignment.

ForEach ($Sku in $SkusToAssign) {
     Write-Host ("Assiging SKU {0} to user {1}" -f $SKU, $User)
     Try {
       $Status = Set-MgUserLicense -UserId $User -AddLicenses @{SkuId = $SKU} -RemoveLicenses @()
     # Remove the assigned license from the available count
       $AvailableLicenses[$SKU]  = ($AvailableLicenses[$SKU] - 1)
     } Catch {
       Write-Host ("Whoops - Error assiging SKU {0} to user {1}" -f $SKU, $User)
     }
}

Figure 1 shows the report generated about actions taken by the script.

Report of license assignment processing showing some successes and some failures
Figure 1: Report of license assignment processing showing some successes and some failures

You can download the complete script from GitHub.

Removing Licenses

We discussed above how to assign multiple licenses with a single command. You can remove multiple licenses with a single command too. The big difference is that the array passed contains just product identifiers rather than hash tables. It’s one of the foibles of the Microsoft Graph PowerShell SDK.

This example shows how to remove two product licenses from a user account:

[array]$LicensesToRemove = "f30db892-07e9-47e9-837c-80727f46fd3d", "6fd2c87f-b296-42f0-b197-1e91e994b900"
Set-MgUserLicense -UserId Lotte.Vetler@office365itpros.com -AddLicenses @() -RemoveLicenses $LicensesToRemove

Plan to Succeed or Fail in License Assignments

A little preparation makes license assignments processed through PowerShell more successful. Other glitches can occur, like transitory network problems or issues affecting the Entra ID back-end license management problem, but the most common problems are the two covered here. Like the other example scripts published by Practical365.com, the code in this article illustrates principles rather than being a complete solution. I hope that you can use the suggestions outlined here.

TEC Talk: Making Generative AI Work for Microsoft 365 Active Directory

AI-based Microsoft 365 Copilot is coming. But before you commit, join this TEC Talk to understand the technology behind Copilot and how it generates information from M365 applications.

About the Author

Tony Redmond

Tony Redmond has written thousands of articles about Microsoft technology since 1996. He is the lead author for the Office 365 for IT Pros eBook, the only book covering Office 365 that is updated monthly to keep pace with change in the cloud. Apart from contributing to Practical365.com, Tony also writes at Office365itpros.com to support the development of the eBook. He has been a Microsoft MVP since 2004.

Leave a Reply