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.
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.