Assigning Licenses to a Bunch of Users
A reader comment for the article covering how to assign licenses to user accounts with the Microsoft Graph PowerShell SDK asked for an example showing how to read accounts from a CSV and assign licenses to those accounts. In other words, how to use PowerShell for bulk assignment of licenses to target user accounts. It’s the kind of thing people do when introducing new products to a tenant.
When Microsoft eventually releases Microsoft 365 Copilot for public consumption, I imagine that organizations willing to stump up the $30/user/month price will look for some sort of bulk assignment mechanism after they decide who gets Copilot licenses. Given that Copilot requires tenants to have Microsoft 365 enterprise licenses, they can use group-based licensing. That’s the best approach and the script discussed here is very much a do-it-yourself assignment mechanism for those who can’t use group-based licensing. Then again, it’s always nice to understand how things work so that you can create your own automation if necessary.
Finding User Accounts to Process
Conceptually, the processing steps are simple. The first step is to import details of target user accounts from a CSV file. Alternatively, you can use another mechanism to establish the set of target accounts. Suitable mechanisms include:
- Membership of a Microsoft 365 group (including dynamic groups). You need to filter the group membership to remove any guest accounts.
- Membership of a distribution list (including dynamic distribution lists). Distribution lists can include objects that can’t be targeted for license assignment, like public folders or mail users, so some filtering is necessary.
- Membership of an Entra ID administrative unit.
- User accounts are found by applying a filter to the Get-MgUser cmdlet.
For example, you could find a set of target accounts by looking for all the accounts located in a certain country:
[array]$Users = Get-MgUser -All -Filter "country eq 'United States'"
Of course, using filters to find user accounts only works if account properties are populated with accurate information.
Creating a Target Users Array for Bulk License Assignment
The point is that it doesn’t matter how you generate a set of target accounts. All that’s important is that your script provides a set of identifiers that can be used for license assignment. Those identifiers can be account object identifiers (GUIDs) or user principal names.
For this example, because so many people use CSV files to point to target accounts, that’s what I do in the example script:
$InputFile = "c:\temp\Users.csv" [array]$Users = Import-CSV $InputFile
The data contained in the $Users array holds the user principal name and display name of the accounts we want to assign licenses to:
UPN DisplayName --- ----------- Lotte.Vetler@office365itpros.com Lotte Vetler Hans.Flick@office365itpros.com Otto Flick James.Ryan@office365itpros.com James Ryan Hans.Geering@office365itpros.com Hans Geering Joe.Sop@contoso.com Joe Sop Ben.James@office365itpros.com Ben James Brian.Weakliam@office365itpros.com Brian Weakliam
Assigning Licenses to Each User Account
The job’s half done when the set of target user accounts is available. After all, the only thing that’s left to do is to run the Set-MgUserLicense cmdlet for each account. Well, that’s certainly true if you want to perform a one-off operation, but it’s best to build some checks and balances in any script code that might be reused.
To illustrate what I mean, examine the processing displayed in Figure 1.
The script:
- Finds the subscribed products (SKUs) known to the tenant and selects the SKUs that still have some available licenses.
- Presents the list of SKUs to the user to allow them to choose which license to assign to the target users.
- Checks that sufficient available licenses exist to assign to all the target users.
- Checks that each target user does not already have the license in their assigned set.
- If the license is not present for a user, attempt to assign the license to the account (note: if your tenant uses restricted administrative units, only accounts holding administrator roles for the administrative units can assign licenses to member accounts).
- Captures details of the success or failure of the license assignment and records the information in a list object (Figure 2).
- Reports details of the processing, including how many successful and failed license assignments occurred.
The Microsoft 365 Kill Chain and Attack Path Management
An effective cybersecurity strategy requires a clear and comprehensive understanding of how attacks unfold. Read this whitepaper to get the expert insight you need to defend your organization!
More Improvements Possible
I spent a happy afternoon playing with the script to anticipate some of the situations that you might encounter during license assignment operations, but I think I barely scratched the surface. There is much more that could be done to expand the script to handle different conditions, such as:
- Assigning multiple product SKUs at one time.
- Disabling service plans that the organization doesn’t want people to use from product SKUs. For instance, the organization might decide that Viva Engage (Yammer) isn’t required and therefore disables the Viva Engage Core service plan in SKUs like Office 365 E3 and E5. That’s possible, but if you disable Viva Engage, you lose some Teams functionality, like the Q&A app in meetings.
- Share the results of the license assignment processing with other people via email or Teams.
Please feel free to amend the script I wrote (available for download from GitHub) to add your ideas. I might disagree with your suggestions, but at least we can have a discussion.
Bulk License Assignment Automated Your Way
PowerShell makes it possible to automate operations the way you want things done rather than the way Microsoft thinks things should be done. That’s its big advantage and it’s the core reason why every Microsoft 365 administrator should be passingly fluent in PowerShell.
If you attend The Experts Conference in Atlanta (Sept 19-20, 2023), make sure to come to the Great PowerShell Script-Off to have some fun and support the contestants as they struggle with the challenges we’ll set for them. All the challenges are common Microsoft 365 tenant automation tasks, so you might even learn something too!
Hi thanks for the info, can you help me?, I’m keep receiving this error: Error: User account does not exist in Entra ID . (Line 96 of the code). I create 3 csv files, also export one from microsoft it self just to test it but still saying the same error.
The Real Person!
Author Tony Redmond acts as a real person and passed all tests against spambots. Anti-Spam by CleanTalk.
Line 96 says: $UserData = Get-MgUser -UserId $User.UPN.Trim() -Property id, assignedLicenses -ErrorAction SilentlyContinue
The data read in for the $User variable from the CSV file is invalid. Does the CSV file have a column header called UPN where all the user principal names (like Tony.Redmond@practical365.com) are stored?
Hi Tony, your script is great, but if i need to disable some function like sway or kaizala?
The Real Person!
Author Tony Redmond acts as a real person and passed all tests against spambots. Anti-Spam by CleanTalk.
https://practical365.com/microsoft-365-license-graph-sdk/
Also missed this part from the error…
Users\IrvensAntoine\My Documents\WindowsPowerShell\Assign-LicensesViaCSV.PS1:76 char:35
Many thanks for creating a script to do what Microsoft doesn’t want us having an easy accessible way to perform actions like this.
Not entirely sure what I’m doing wrong, but I continue to receive this error when I try to execute the script.
+ If ($LicensesAvailable -eq $True) {
+ ~
Missing closing ‘}’ in statement block or type definition.
+ CategoryInfo : ParserError: (:) [], ParseException
+ FullyQualifiedErrorId : MissingEndCurlyBrace
I didn’t think anything in the script needed to be changed, as side from Input File location. Any tips on what I need to do to get past this?
Many thanks in advance.
The Real Person!
Author Tony Redmond acts as a real person and passed all tests against spambots. Anti-Spam by CleanTalk.
Please download a revised version of the script from GitHub. I found the problem…
Greetings.
Thank you for your work, a very necessary thing.
Tell me if the list does not show all the SKUs present in the subscription. It turns out that not everything is shown when choosing a destination. Are there any restrictions?
Thank you in advance!
The Real Person!
Author Tony Redmond acts as a real person and passed all tests against spambots. Anti-Spam by CleanTalk.
The Get-MgSubscribedSku cmdlet returns all the product licenses that a tenant has subscribed to. Is that what you mean?
Hi Tony,
I work in a school enviornment and the students are assigned with one the below license. I want to disable the Teams license from the students account listed in a csv file. I have to enable the Teams license later. Students UserPrincipalName are saved in this location: C:\Data\StudentsToDisable.CSV
Please help me with the powershell script
‘M365EDU_A5_STUUSEBNFT’
‘ENTERPRISEPACKPLUS_STUDENT’
‘STANDARDWOFFPACK_STUDENT’
‘M365EDU_A3_STUUSEBNFT’
‘STANDARDWOFFPACK_IW_STUDENT’
The Real Person!
Author Tony Redmond acts as a real person and passed all tests against spambots. Anti-Spam by CleanTalk.
There are many script examples that are available on the internet that you can use as a basis. You don’t say how you know which license to assign to an account. Assuming that this information is in the CSV file, I would:
• Connect to the Graph with Connect-MgGraph. Make sure that the scope set is User.ReadWrite.All.
• Import the user data from the CSV file with Import-CSV.
• For each user, run Set-MgUserLicense to assign the license and disable the Teams service plan (57ff2da0-773e-42df-b2af-ffb7a2317929). Make sure that you know the GUID for each product license you plan to use, like 31d57bc7-3a05-4867-ab53-97a17835a411 for M365EDU_A5_STUUSEBNFT.
Product GUIDs are available from https://learn.microsoft.com/en-us/entra/identity/users/licensing-service-plan-reference.
I keep getting this error. I ran it with just changing the filename to mine & even tried the snippet above. Same error
WARNING: One or more headers were not specified. Default names starting with “H” have been used in place of any missing headers.
Cannot index into a null array.
At line:76 char:5
+ Write-Host (“{0} users are to receive licenses but there are only …
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : NullArray
The Real Person!
Author Tony Redmond acts as a real person and passed all tests against spambots. Anti-Spam by CleanTalk.
Looks like the Import-CSV from the file pointed to with $InputFile didn’t bring in any user accounts to process.
Hi Tony, thank you for the very intersting script. I’m trying to use only a part of it as my task is a one shot group add of licences, but I am stuck with an error.
Here is the script I adapted:
$InputFile = “c:\Users\damien.hartmann\Documents\comptes.csv”
[array]$Users = Import-CSV $InputFile
ForEach ($User in $Users) { $License = Set-MgUserLicense -UserId $User.UPN -Addlicenses @{SkuId = ‘$18181a46-0d4e-45cd-891e-60aabd171b4e’}
}
Would you by chance see where I made a mistake?
The Real Person!
Author Tony Redmond acts as a real person and passed all tests against spambots. Anti-Spam by CleanTalk.
Your SKUID has a $ sign in the first character. That makes it an invalid GUID
Thanks, but I still get the same error message:
Set-MgUserLicense : Impossible de lier l’argument au paramètre « UserId », car il s’agit d’une chaîne vide.
Au caractère C:\Users\damien.hartmann\Documents\WindowsPowerShell\ajouter_licences_v3.ps1:3 : 66
+ … r in $Users) { $License = Set-MgUserLicense -UserId $User.UPN -Addlic …
+ ~~~~~~~~~
+ CategoryInfo : InvalidData : (:) [Set-MgUserLicense], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorEmptyStringNotAllowed,Set-MgUserLicense
(sorry for the French)
Finally I managed to make it work, thanks again!
The Real Person!
Author Tony Redmond acts as a real person and passed all tests against spambots. Anti-Spam by CleanTalk.
Great!
Hey can you send me the code you used
Hi Tony, I found the script, whoch is working fine for me. Thank you
Hi Tony, The arcticle is elaborate an dvery helpfull, please share the Script that does the output shown in powershell above
The Real Person!
Author Tony Redmond acts as a real person and passed all tests against spambots. Anti-Spam by CleanTalk.
The script is available in GitHub and is linked to in the article.