Public/Report/Get-OCM365UserLicense.ps1

function Get-OCM365UserLicense {
    <#
    .SYNOPSIS
        Retrieves license assignments for all users with licenses.

    .DESCRIPTION
        Gets all users who have licenses assigned and returns their license information.
        Returns user information along with their assigned licenses in a user-centric view.
        Optionally includes friendly names for licenses using the Get-OCM365MicrosoftLicenses function.

    .PARAMETER IncludeFriendlyNames
        If specified, includes friendly license names from the Microsoft licensing reference data.
        Otherwise, only the SkuPartNumber is used.

    .EXAMPLE
        Get-OCM365UserLicense
        Retrieves all users and their license assignments.

    .EXAMPLE
        Get-OCM365UserLicense -IncludeFriendlyNames
        Retrieves all users and their licenses with friendly names.

    .NOTES
        Requires Microsoft Graph PowerShell module and an active Graph connection.
        Required Graph API permissions: Organization.Read.All, User.Read.All
    #>

    [CmdletBinding()]
    param (
        [Parameter()]
        [switch]$IncludeFriendlyNames
    )

    begin {
        Write-Debug "[Get-OCM365UserLicense] Starting function with IncludeFriendlyNames: $IncludeFriendlyNames"
        
        # Check if connected to Microsoft Graph
        $mgContext = Get-MgContext
        if (-not $mgContext) {
            Write-Error "Not connected to Microsoft Graph. Please run Connect-MgGraph first." -ErrorAction Stop
            throw "Not connected to Microsoft Graph. Please run Connect-MgGraph first."
        }
        
        Write-Debug "[Get-OCM365UserLicense] Successfully validated Graph connection"

        # Check required permissions using the permission hierarchy function
        $requiredScopes = @('Organization.Read.All', 'User.Read.All')
        if (-not (Test-OCM365GraphPermission -RequiredPermissions $requiredScopes -Scopes $mgContext.Scopes)) {
            $message = "Missing required permissions: $($requiredScopes -join ', '). Current scopes: $($mgContext.Scopes -join ', ')"
            Write-Error $message -ErrorAction Stop
            throw $message
        }
        
        Write-Debug "[Get-OCM365UserLicense] Permission validation successful"

        # Prepare friendly names lookup if requested
        $friendlyNamesLookup = @{}
        if ($IncludeFriendlyNames) {
            Write-Debug "[Get-OCM365UserLicense] Retrieving friendly names from Microsoft licensing reference"
            try {
                $allLicenses = Get-OCM365MicrosoftLicenses -ErrorAction Stop
                $friendlyNamesLookup = @{}
                foreach ($license in $allLicenses) {
                    $friendlyNamesLookup[$license.SkuId] = $license.DisplayName
                }
                Write-Debug "[Get-OCM365UserLicense] Loaded $($friendlyNamesLookup.Count) friendly license names"
            }
            catch {
                Write-Warning "Failed to retrieve friendly license names: $_"
                Write-Debug "[Get-OCM365UserLicense] Continuing with SkuPartNumber only"
            }
        }
        
        Write-Information "Starting user license retrieval" -InformationAction $InformationPreference
    }

    process {
        Write-Verbose "Retrieving all licenses and user mappings..." -Verbose:$VerbosePreference
        
        try {
            Write-Debug "[Get-OCM365UserLicense] Calling Get-MgSubscribedSku"
            
            # Get all licenses with their assignments
            $Licenses = Get-MgSubscribedSku -ErrorAction Stop -Verbose:$VerbosePreference -Debug:$DebugPreference | ForEach-Object {
                Write-Progress -Activity "Processing Licenses" -Status "Processing $($_.SkuPartNumber)" -ProgressAction $ProgressPreference
                Write-Debug "[Get-OCM365UserLicense] Processing license: $($_.SkuPartNumber)"
                
                # Get friendly name if available
                $friendlyName = $_.SkuPartNumber
                if ($IncludeFriendlyNames -and $friendlyNamesLookup.ContainsKey($_.SkuId)) {
                    $friendlyName = $friendlyNamesLookup[$_.SkuId]
                    Write-Debug "[Get-OCM365UserLicense] Got friendly name: $friendlyName"
                }
                
                # Get assigned users
                Write-Debug "[Get-OCM365UserLicense] Getting assigned users for SKU: $($_.SkuId)"
                $assignedUsers = Get-MgUser -Filter "assignedLicenses/any(u:u/skuId eq $($_.SkuId))" -All -ErrorAction Stop -Verbose:$VerbosePreference -Debug:$DebugPreference
                
                [PSCustomObject]@{
                    SkuId         = $_.SkuId
                    SkuPartNumber = $_.SkuPartNumber
                    FriendlyName  = $friendlyName
                    AssignedUsers = $assignedUsers
                }
            }
            
            Write-Progress -Activity "Processing Licenses" -Completed -ProgressAction $ProgressPreference
            Write-Verbose "Retrieved $($Licenses.Count) licenses" -Verbose:$VerbosePreference
            Write-Debug "[Get-OCM365UserLicense] License retrieval complete"
            
            # Get unique users
            Write-Verbose "Getting unique assigned users and building user-license mapping..." -Verbose:$VerbosePreference
            Write-Debug "[Get-OCM365UserLicense] Extracting unique users from licenses"
            
            $Users = $Licenses | 
                Select-Object -ExpandProperty AssignedUsers | 
                Sort-Object -Property Id -Unique
            
            Write-Information "Found $($Users.Count) unique users with licenses" -InformationAction $InformationPreference
            Write-Verbose "Found $($Users.Count) unique users with licenses" -Verbose:$VerbosePreference
            Write-Debug "[Get-OCM365UserLicense] User extraction complete"
            
            # Create user-centric output
            Write-Debug "[Get-OCM365UserLicense] Building user-centric license mapping"
            
            $results = foreach ($user in $Users) {
                Write-Progress -Activity "Processing Users" -Status "Processing $($user.UserPrincipalName)" -ProgressAction $ProgressPreference
                Write-Debug "[Get-OCM365UserLicense] Processing user: $($user.UserPrincipalName)"
                
                $assignedLicenses = $Licenses | 
                    Where-Object { $_.AssignedUsers.Id -contains $user.Id } | 
                    Select-Object SkuId, SkuPartNumber, FriendlyName
                
                [PSCustomObject]@{
                    UserPrincipalName = $user.UserPrincipalName
                    DisplayName       = $user.DisplayName
                    Mail              = $user.Mail
                    AssignedLicenses  = $assignedLicenses
                }
            }
            
            Write-Progress -Activity "Processing Users" -Completed -ProgressAction $ProgressPreference
            Write-Information "Processed $($results.Count) users with license information" -InformationAction $InformationPreference
            Write-Verbose "Processed $($results.Count) users with license information" -Verbose:$VerbosePreference
            Write-Debug "[Get-OCM365UserLicense] User processing complete"
            
            return $results
        }
        catch {
            Write-Error "Failed to retrieve user license information: $_" -ErrorAction Stop
            throw
        }
    }
}