The Challenge of License Management at Scale
Managing Microsoft 365 licenses individually becomes unwieldy as organizations grow. Each time an employee joins, changes roles, or leaves, IT must manually update licenses. Group-based licensing solves this by automatically assigning licenses based on group membership.
Prerequisites
- Azure AD Premium P1 or P2 license
- Global Administrator or License Administrator role
- PowerShell modules:
AzureADorMicrosoft.Graph
Step 1: Audit Current License Assignments
First, identify all licenses currently assigned individually:
# Connect to Azure AD
Connect-AzureAD
# Get all users with direct license assignments
$users = Get-AzureADUser -All $true | Where-Object {$_.AssignedLicenses.Count -gt 0}
# Export current state
$licenseReport = @()
foreach ($user in $users) {
foreach ($license in $user.AssignedLicenses) {
$licenseReport += [PSCustomObject]@{
UserPrincipalName = $user.UserPrincipalName
DisplayName = $user.DisplayName
SkuId = $license.SkuId
}
}
}
$licenseReport | Export-Csv "DirectLicenseAssignments.csv" -NoTypeInformation
Step 2: Map SKU IDs to Friendly Names
Create a mapping of Microsoft SKU IDs to group names:
# Define license mapping
$licenseMapping = @{
"DYN365_BUSCENTRAL_ESSENTIAL" = "License-DynamicsBusinessCentralEssential"
"EXCHANGESTANDARD" = "License-ExchangeOnlinePlan1"
"ENTERPRISEPACK" = "License-O365E3"
"AAD_PREMIUM_P2" = "License-EntraIDP2"
"POWER_BI_PRO_CE" = "License-PowerBIPro"
"PROJECTPROFESSIONAL" = "License-PlannerProjectPlan3"
"POWERAPPS_PER_APP" = "License-PowerAppsPerAppBaseline"
"POWERAUTOMATE_ATTENDED_RPA" = "License-PowerAutomatePremium"
}
Step 3: Create License Groups
foreach ($license in $licenseMapping.GetEnumerator()) {
$groupName = $license.Value
# Check if group exists
$existingGroup = Get-AzureADGroup -Filter "DisplayName eq '$groupName'"
if (-not $existingGroup) {
# Create new group
New-AzureADGroup `
-DisplayName $groupName `
-MailEnabled $false `
-SecurityEnabled $true `
-MailNickname $groupName.Replace(" ", "")
Write-Host "Created group: $groupName" -ForegroundColor Green
}
}
Step 4: Assign Licenses to Groups
# Get available licenses
$subscribedSkus = Get-AzureADSubscribedSku
foreach ($license in $licenseMapping.GetEnumerator()) {
$skuPartNumber = $license.Key
$groupName = $license.Value
# Find the SKU
$sku = $subscribedSkus | Where-Object {$_.SkuPartNumber -eq $skuPartNumber}
if ($sku) {
# Get the group
$group = Get-AzureADGroup -Filter "DisplayName eq '$groupName'"
# Create license assignment
$licensesToAssign = New-Object -TypeName Microsoft.Open.AzureAD.Model.AssignedLicense
$licensesToAssign.SkuId = $sku.SkuId
$licensesToRemove = New-Object -TypeName Microsoft.Open.AzureAD.Model.AssignedLicenses
$licensesToRemove.AddLicenses = $licensesToAssign
# Assign license to group
Set-AzureADGroupLicense -ObjectId $group.ObjectId -AssignedLicenses $licensesToRemove
Write-Host "Assigned $skuPartNumber to group $groupName" -ForegroundColor Green
}
}
Step 5: Migrate Users to Group-Based Licensing
# Process each user
foreach ($user in $users) {
$userLicenses = Get-AzureADUserLicenseDetail -ObjectId $user.ObjectId
foreach ($userLicense in $userLicenses) {
# Find corresponding group
$groupName = $licenseMapping[$userLicense.SkuPartNumber]
if ($groupName) {
$group = Get-AzureADGroup -Filter "DisplayName eq '$groupName'"
# Add user to group
Add-AzureADGroupMember -ObjectId $group.ObjectId -RefObjectId $user.ObjectId
Write-Host "Added $($user.UserPrincipalName) to $groupName" -ForegroundColor Yellow
}
}
}
Step 6: Verify and Remove Direct Assignments
Wait for license processing (usually 2-4 hours), then verify:
# Verify group license assignment
foreach ($groupName in $licenseMapping.Values) {
$group = Get-AzureADGroup -Filter "DisplayName eq '$groupName'"
$members = Get-AzureADGroupMember -ObjectId $group.ObjectId
foreach ($member in $members) {
$licenses = Get-AzureADUserLicenseDetail -ObjectId $member.ObjectId
foreach ($license in $licenses) {
if ($license.AssignmentPaths -contains "Group") {
Write-Host "$($member.UserPrincipalName) has group-assigned $($license.SkuPartNumber)" -ForegroundColor Green
}
}
}
}
# Remove direct assignments (after verification)
foreach ($user in $users) {
$licensesToRemove = New-Object -TypeName Microsoft.Open.AzureAD.Model.AssignedLicenses
$licensesToRemove.RemoveLicenses = $user.AssignedLicenses.SkuId
Set-AzureADUserLicense -ObjectId $user.ObjectId -AssignedLicenses $licensesToRemove
}
Best Practices
1. Use Descriptive Naming Conventions
Prefix license groups with “License-” for easy identification.
2. Implement Role-Based Groups
Create groups based on job roles:
# Example: Sales team gets E3 + Power BI
$salesGroup = New-AzureADGroup -DisplayName "Role-Sales" -MailEnabled $false -SecurityEnabled $true
Add-AzureADGroupMember -ObjectId (Get-AzureADGroup -Filter "DisplayName eq 'License-O365E3'").ObjectId -RefObjectId $salesGroup.ObjectId
Add-AzureADGroupMember -ObjectId (Get-AzureADGroup -Filter "DisplayName eq 'License-PowerBIPro'").ObjectId -RefObjectId $salesGroup.ObjectId
3. Monitor License Usage
# Create usage report
$usageReport = @()
foreach ($sku in Get-AzureADSubscribedSku) {
$usageReport += [PSCustomObject]@{
License = $sku.SkuPartNumber
Total = $sku.PrepaidUnits.Enabled
Assigned = $sku.ConsumedUnits
Available = $sku.PrepaidUnits.Enabled - $sku.ConsumedUnits
}
}
$usageReport | Format-Table -AutoSize
4. Handle License Conflicts
When users need multiple licenses with conflicting services:
# Disable specific services
$disabledPlans = @("TEAMS1", "YAMMER_ENTERPRISE")
$license = New-Object -TypeName Microsoft.Open.AzureAD.Model.AssignedLicense
$license.SkuId = $sku.SkuId
$license.DisabledPlans = $disabledPlans
Automation with Azure Automation
Create a runbook for automatic license assignment based on attributes:
param(
[Parameter(Mandatory=$true)]
[string]$UserPrincipalName
)
# Connect using Managed Identity
Connect-AzureAD -Identity
$user = Get-AzureADUser -ObjectId $UserPrincipalName
$department = $user.Department
# Department-based assignment
$departmentMapping = @{
"Sales" = @("License-O365E3", "License-PowerBIPro")
"Engineering" = @("License-O365E3", "License-VisioPlan1", "License-PlannerProjectPlan3")
"Finance" = @("License-O365E3", "License-DynamicsBusinessCentralEssential")
}
if ($departmentMapping.ContainsKey($department)) {
foreach ($groupName in $departmentMapping[$department]) {
$group = Get-AzureADGroup -Filter "DisplayName eq '$groupName'"
Add-AzureADGroupMember -ObjectId $group.ObjectId -RefObjectId $user.ObjectId
}
}
Conclusion
Group-based licensing transforms license management from a manual, error-prone process to an automated, scalable solution. By implementing this approach, organizations can ensure consistent licensing, reduce administrative overhead, and improve compliance tracking.