Public/Get-M365UserSegments.ps1
|
function Get-M365UserSegments { <# .SYNOPSIS Retrieves license-assignment group memberships from Microsoft Graph for user segmentation. .DESCRIPTION Finds all Entra ID groups that have licenses assigned (group-based licensing), retrieves each group's assigned licenses and members, and outputs one row per user-group pair. This enables segment-based license optimization analysis in Kusto. .PARAMETER AccessToken Microsoft Graph API access token. .OUTPUTS [PSCustomObject[]] One object per user per segment group. #> [CmdletBinding()] param( [Parameter(Mandatory)] [string]$AccessToken ) # Build SKU cache for friendly names Write-M365Log "Building SKU reference cache for segment licenses..." $skuCache = @{} try { $skus = Invoke-M365GraphRequest -AccessToken $AccessToken -Uri "https://graph.microsoft.com/v1.0/subscribedSkus" -AllPages foreach ($sku in $skus) { $skuCache[$sku.skuId] = $sku.skuPartNumber } Write-M365Log "Cached $($skuCache.Count) SKUs" } catch { Write-M365Log "Warning: Could not retrieve SKUs: $_" -Level Warning } # Retrieve all groups with assignedLicenses property, then filter client-side. # Server-side filtering on assignedLicenses/$count requires advanced query capabilities # which may not be available in all tenants. $groupUri = "https://graph.microsoft.com/v1.0/groups?`$select=id,displayName,assignedLicenses&`$top=999" Write-M365Log "Retrieving groups from Microsoft Graph..." $allGroups = Invoke-M365GraphRequest -AccessToken $AccessToken -Uri $groupUri -AllPages Write-M365Log "Retrieved $($allGroups.Count) total groups" # Keep only groups that have at least one license assigned $groups = $allGroups | Where-Object { $_.assignedLicenses -and $_.assignedLicenses.Count -gt 0 } Write-M365Log "Found $($groups.Count) groups with license assignments" $ingestionTime = (Get-Date).ToUniversalTime().ToString('o') $totalMembers = 0 foreach ($group in $groups) { $groupId = $group.id $groupDisplayName = $group.displayName # Resolve assigned licenses to SKU part numbers $groupLicenses = @() if ($group.assignedLicenses) { foreach ($lic in $group.assignedLicenses) { $skuName = $skuCache[$lic.skuId] if (-not $skuName) { $skuName = $lic.skuId } $groupLicenses += @{ skuId = $lic.skuId skuPartNumber = $skuName } } } $licensesJson = if ($groupLicenses.Count -gt 0) { ($groupLicenses | ConvertTo-Json -Compress -Depth 5) } else { "[]" } # Get group members (users only) $memberUri = "https://graph.microsoft.com/v1.0/groups/$groupId/members/microsoft.graph.user?`$select=id,userPrincipalName&`$top=999" $members = Invoke-M365GraphRequest -AccessToken $AccessToken -Uri $memberUri -AllPages Write-M365Log " $groupDisplayName : $($members.Count) members, $($groupLicenses.Count) licenses" foreach ($member in $members) { $totalMembers++ [PSCustomObject]@{ GroupId = $groupId GroupDisplayName = $groupDisplayName GroupLicenses = $licensesJson UserId = $member.id UserPrincipalName = $member.userPrincipalName IngestionTime = $ingestionTime } } } Write-M365Log "Emitted $totalMembers user-segment records to pipeline" } |