A Quick Refresher for Group-Based Licensing
In February 2017, Paul Cunningham reviewed group-based licensing. Apart from renaming Azure AD to Entra ID, making cosmetic updates to the admin center GUI, and refreshing the documentation, not much has changed in the way the solution works.
In 2021, Microsoft said that they would remove the requirement for Entra ID Premium P1 licenses following the transition to a new license management platform and support nested groups. The transition to the new platform has happened and no trace of the requirement is in the documentation, so that’s a welcome development that might make group-based licensing a more attractive option for tenants.
Group-based licensing is a simple and effective method to assign licenses. In a nutshell, it works as follows:
- Licenses and their options (service plans) are associated with a security group.
- As users join the group, Entra ID assigns the associated licenses to their account. If a user leaves the group, Entra ID removes the license.
- The scheme works as long as licenses are available for assignment. If a license isn’t available, Entra ID can’t assign a license until the tenant either buys more licenses or removes a license from another account.
- The only other problem is conflicting service plans. This occurs when Entra ID attempts to assign a license that includes a service plan that’s already available to the account via another license. The problem was most often seen with Exchange Online because many products include an Exchange Online Plan 1 or Plan 2 service plan. The advent of license stacking for Exchange Online in early 2023 removed this problem.
For the purpose of this discussion, I created a security group called Microsoft Fabric License Assignment Group and associated it with licenses for Microsoft Fabric and Power Automate (Figure 1). Members added to the group receive these licenses and the service plans enabled by the licenses.
Group-Based License Management
This article covers the basics of license management for Entra ID accounts using the Microsoft Graph PowerShell SDK. The assignments described in the text are direct, meaning that administrators or scripts run the Set-MgUserLicense cmdlet to add or remove licenses or service plans for user accounts.
With group-based licensing in use, direct assignments are not used. Instead, you add an account to the group used to control license assignments. For example, these commands find the identifiers for the target group and the user account to add and run the New-MgGroupMember cmdlet to add the member to the group. They’re examples of the kind of processing that you might do after adding a new account.
$GroupId = (Get-MgGroup -Filter "displayName eq 'Microsoft Fabric License Assignment Group'").Id $UserId = (Get-MgUser -UserId Michelle.Dubois@office365itpros.com).Id New-MgGroupMember -GroupId $GroupId -DirectoryObjectId $UserId
A couple of minutes later (assuming licenses are available), the account has the licenses assigned through group membership. As you can see from Figure 2, the account has one direct assignment and two inherited from the group.
When creating new accounts, it’s important that these accounts receive licenses as soon as possible as otherwise they cannot access any services. For this reason, any interruption to normal group-based license assignments should be investigated and rectified quickly.
To remove licenses assigned through a group, remove the account from the group membership using the Remove-MgGroupMemberByRef cmdlet:
Remove-MgGroupMemberByRef -DirectoryObjectId $UserId -GroupId $GroupId
Following the removal of the account from the group membership, Entra ID removes the licenses from the account within a couple of minutes. See this article for more information about group management using the Microsoft Graph PowerShell SDK.
Reporting Group-Based Licensing
Two years ago, I discussed how to create a licensing report for a tenant with Microsoft Graph PowerShell SDK cmdlets. That version of the report only handled direct license assignments. To upgrade the report to handle group-based licensing, the script that analyzes license data checks the licenseAssignmentStates array returned by the Get-MgUser cmdlet.
When a group-based assignment exists, the array holds information about the assigning group and the product identifier. An entry in the array is present for each product license assigned through group-based licensing.
AssignedByGroup : ebe02ef6-c696-4cdb-92d1-011a6c2a02d2 DisabledPlans : {} Error : None LastUpdatedDateTime : 29/09/2023 20:25:31 SkuId : f30db892-07e9-47e9-837c-80727f46fd3d State : Active AdditionalProperties : {}
It’s possible for an account to have licenses assigned directly and via a group. This doesn’t interfere with the services enabled by the licenses but duplicate assignments are a waste of licenses.
To include the data in the report, the script extracts the group assignments and loops through the set to resolve the group display name and product “friendly” name (like Office 365 E3). This information is then formatted and put into a variable that is subsequently included in the report. Here’s the loop to extract the group-based licensing assignments:
[array]$GroupAssignments = $User.licenseAssignmentStates | Where-Object {$Null -ne $_.AssignedByGroup -and $_.State -eq "Active"} [array]$GroupLicensing = $Null # Figure out group-based licensing assignments if any exist ForEach ($G in $GroupAssignments) { $GroupName = (Get-MgGroup -GroupId $G.AssignedByGroup).DisplayName $GroupProductName = $SkuHashTable[$G.SkuId] $GroupLicensing += ("{0} assigned from {1}" -f $GroupProductName, $GroupName) } $GroupLicensingAssignments = $GroupLicensing -Join ", "
Figure 3 shows how group-based licensing assignments show up in the output report.
As a bonus, the report tells you when it detects duplicate license assignments and highlights accounts that haven’t signed in recently. Administrators can use this information to remove duplicate licenses and consider if the licenses assigned to accounts that aren’t in active use can either be removed or replaced with a lower license. For instance, you might be able to assign an Office 365 F1 license to an account and remove its Office 365 E3 or E5 license. Finally, the report summarizes the product SKUs assigned to users within the tenant (Figure 4).
You can download the updated script from GitHub. If you’ve downloaded the script before, make sure that you have version 1.3 or later.
Update: The latest version of the script includes code to report license costs. See this article for more information.
Group-Based Licensing Worth Considering
Group-based licensing isn’t difficult to set up or manage. It’s definitely a method worth considering for license management if you’re confident that license availability won’t be a problem. If you haven’t tried group-based licensing yet, why not start with a relatively unimportant license and see whether this mechanism fits into your tenant management strategy. And when you’ve assigned some licenses with groups, remember to run the report to see if any duplicate assignments exist.
Microsoft Platform Migration Planning and Consolidation
Simplify migration planning, overcome migration challenges, and finish projects faster while minimizing the costs, risks and disruptions to users.
Hi Tony
Because we use the groups to manage the license, can I change and get the group assigned with one license and service plan?
Can you change the code? Certainly. It’s just PowerShell. The question is what do you want to appear in the report…
Hi Tony,
I got an error on line 19. Try to create an index of a NULL-Array.
Es ist nicht möglich, einen Index auf ein NULL-Array anzuwenden.
In C:\Temp\ReportUserAssignedLicenses-MgGraph.PS1:19 Zeichen:11
+ [string]$LicenseCost = $PricingHashTable[$License]
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : NullArray
Any ideas?
The license GUID pointed to in the $License variable does not exist in the $PricingHashTable. Adding cost details to the script was a recent upgrade (see https://practical365.com/report-user-license-costs/). Have a look at the CSV file containing subscription info and check the licenses assigned to the user being processed to find the problem license.
Hi Tony,
Is it possible to mix month-to-month and yearly licenses of the same type (Microsoft 365 standard for ex)? Does entraID differentiate those, or it’s just the license type what matters. For ex. we have 10 yearly and 10 monthly licenses, and 5 teams of 6 teams with 3 members each. When we are assigning licenses to those teams, can we just assign Standard license to each team, and EntraID will take what’s needed? Or ween need to assign monthly license to one team, and yearly to another?
AFAIK, Entra ID doesn’t distinguish monthly and yearly licenses when it comes to the license assigned to accounts, so you should be able to issue licenses via direct and group-based assignments as you want. The issue arises in the subscription renewal date. Monthly licenses obviously can expire sooner…
Thank you very much for the script, it helps me a lot. I have one more suggestion: Would it be possible to include a check to see if a user really has a license or if it was just assigned to him by a group but he didn’t get it because of missing licenses? Then it would fulfill all the requirements I need
A user has a license or they don’t. There are no phantom licenses to check…
What you could do is check for user membership in groups used to assign licenses and then against the set of assigned licenses held by the account. If the licenses differ, you know you’ve got a problem with group-based licensing.
Any idea what could generate this error and how I can resolve it?
Cannot convert value “27-Dec-2023 15:41:01” to type “System.Int32”. Error: “The input string ’27-Dec-2023 15:41:01′ was not in a correct format.”
That’s an odd error because it indicates an attempt to convert a date into an integer. When in the script does this occur?
Cannot convert value “05-Jan-2024 12:32:49” to type “System.Int32”. Error: “Input string was not in a correct format.”
At C:\Users\OneDrive\_PowerShell\ReportUserAssignedLicenses-MgGraph\ReportUserAssignedLicenses-MgGraph.PS1:101 char:7
+ $DaysSinceLastSignIn = ($RunDate – $LastSignInDate).Days
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvalidCastFromStringToInteger
I’m in the netherlands, could it be a time zone/locality thingy?
I suspect it’s a local configuration setting for PowerShell. The date looks fine in terms of being in an English format. This format might be unacceptable for PowerShell configured to use the Dutch locale. See https://devblogs.microsoft.com/scripting/use-culture-information-in-powershell-to-format-dates/ and https://devblogs.microsoft.com/scripting/formatting-date-strings-with-powershell/
Replacing [string] with [datetime] in line 11 solved the problem for me!
Looking at this again, the string assignment
[string]$RunDate = Get-Date -format “dd-MMM-yyyy HH:mm:ss”
Might not work in non-English locales. Changing it to an explicit date/time would make it work. The variable is only used in report output, so it can be in whatever format you want…
In my tenant, almost everything is reported as duplicated.
I think this is because skus assigned by a group pop up in $User.AssignedLicenses
I found some bugs that I fixed in version 1.4. Please download from GitHub and give the code a try out.
First and foremost, I’d like to express my gratitude for your continuous contributions to the community. Your work has been instrumental in aiding many individuals and organizations in managing their IT resources more effectively.
I downloaded Version 1.3 today and wanted your thoughts regarding a challenge I encountered while utilizing your ReportUserAssignedLicenses-MgGraph.PS1 script. Specifically, I was testing the script and noticed an issue with the output data in the Microsoft365LicensesReport.CSV (and .HTML). It appears that the license value is present in the “Direct assigned licenses” column for all users (which I know is not accurate in my tenant), and I was hoping to understand whether this is the expected behavior or point me in the direction to get this resolved?
Hi Robert,
I think I need some more information from you to fully understand what issue you’re encountering. The value in the Direct assigned licenses column in the set of licenses directly assigned (by admins) to a user account. I see the correct licenses for the users shown for my tenant. You seem to be saying that you see an incorrect value. If so, can you run the Get-MgUserLicenseDetail cmdlet for one of the affected accounts and report the output here.
TR
PS C:\Windows\system32> get-mguserlicenseDetail -UserId “Edward#######” | ft
Id SkuId SkuPartNumber
— —– ————-
hr6frJHNBUGIN57CVSj4Su7E6wa1G91HgSARMkvFTgY 06ebc4ee-1bb5-47dd-8120-11324bc54e06 SPE_E5
Try Version 1.4 that’s now in GitHub.
For instance, if you look at the Direct assigned licenses column shown in Figure 1 in the article, you see that different licenses are reported for different accounts. These values come from Get-MgLicenseDetail.
Hi Tony!
I was reading the document https://learn.microsoft.com/en-us/azure/active-directory/fundamentals/concept-group-based-licensing and unfortunately it is a requirement to still have Microsoft Entra ID P1 and above or Microsoft licenses 365 Business Premium or Office 365 Enterprise E3 or Office 365 A3 or Office 365 GCC G3 or Office 365 E3 for GCCH or Office 365 E3 for DOD and above.
Yes, that’s the current position. But it’s also true that Microsoft made a commitment to remove the Premium P1 requirement when they migrate Entra ID to a new licensing platform. According to Alex Simons, VP for Marketing in Entra ID, that work is still progressing, but it will conclude sometime. The only question is when.