Scripts/Get-ProductLicenses.ps1

Function Get-Licenses {
<#
    .SYNOPSIS
    Retrieves all licenses in the tenant with retention times and premium license indicators.
 
    .DESCRIPTION
    Returns all available licenses in the tenant along with their retention times and premium license indicators, and exports the details to a CSV file.
 
    .PARAMETER OutputDir
    OutputDir is the parameter specifying the output directory.
    Default: Output\Licenses
 
    .PARAMETER LogLevel
    Specifies the level of logging:
    None: No logging
    Minimal: Critical errors only
    Standard: Normal operational logging
    Debug: Verbose logging for debugging purposes
    Default: Standard
 
    .EXAMPLE
    Get-Licenses
    Retrieves all licenses and saves them to a CSV file in Output\Licenses.
#>

    [CmdletBinding()]
    param(
        [string]$OutputDir = "Output\Licenses",
        [ValidateSet('None', 'Minimal', 'Standard', 'Debug')]
        [string]$LogLevel = 'Standard'
    )

    Set-LogLevel -Level ([LogLevel]::$LogLevel)
    $isDebugEnabled = $script:LogLevel -eq [LogLevel]::Debug

    if ($isDebugEnabled) {
        Write-LogFile -Message "[DEBUG] PowerShell Version: $($PSVersionTable.PSVersion)" -Level Debug
        Write-LogFile -Message "[DEBUG] Input parameters:" -Level Debug
        Write-LogFile -Message "[DEBUG] OutputDir: '$OutputDir'" -Level Debug
        Write-LogFile -Message "[DEBUG] LogLevel: '$LogLevel'" -Level Debug
        
        $graphModules = Get-Module -Name Microsoft.Graph* -ErrorAction SilentlyContinue
        if ($graphModules) {
            Write-LogFile -Message "[DEBUG] Microsoft Graph Modules loaded:" -Level Debug
            foreach ($module in $graphModules) {
                Write-LogFile -Message "[DEBUG] - $($module.Name) v$($module.Version)" -Level Debug
            }
        } else {
            Write-LogFile -Message "[DEBUG] No Microsoft Graph modules loaded" -Level Debug
        }
    }

    if (!(Test-Path $OutputDir)) {
        New-Item -ItemType Directory -Force -Path $OutputDir | Out-Null
    }

    Write-LogFile -Message "=== Starting License Collection ===" -Color "Cyan" -Level Standard

    try {
        $licenses = Get-MgSubscribedSku | Select-Object SkuPartNumber, CapabilityStatus, AppliesTo, ConsumedUnits, ServicePlans

        if ($isDebugEnabled) {
            Write-LogFile -Message "[DEBUG] Retrieved licenses:" -Level Debug
            foreach ($license in $licenses) {
                Write-LogFile -Message "[DEBUG] - $($license.SkuPartNumber): $($license.ConsumedUnits) units, Status: $($license.CapabilityStatus)" -Level Debug
            }
        }

        if (-not $licenses) {
            Write-LogFile -Message "[ERROR] No licenses found in the tenant." -Color "Red" -Level Minimal
            return
        }

        $results = $licenses | ForEach-Object {
             $servicePlanNames = $_.ServicePlans.ServicePlanName -join '; '
             $servicePlansForChecks = $_.ServicePlans.ServicePlanName

             if ($isDebugEnabled) {
                Write-LogFile -Message "[DEBUG] Service Plans Count: $($_.ServicePlans.Count)" -Level Debug
                Write-LogFile -Message "[DEBUG] Service Plans: $($servicePlansForChecks -join ', ')" -Level Debug
            }


            [PSCustomObject]@{
                Sku              = $_.SkuPartNumber
                Status           = $_.CapabilityStatus
                Scope            = $_.AppliesTo
                Units            = $_.ConsumedUnits
                Retention        = if ($_.SkuPartNumber -match "E5") { "365 days" }
                                  elseif ($_.SkuPartNumber -match "E3") { "180 days" }
                                  else { "90 days" }
                E3               = if ($_.SkuPartNumber -in @("M365ENTERPRISE", "ENTERPRISEPACK", "STANDARD_EDU")) { "Yes" } else { "No" }
                E5               = if ($_.SkuPartNumber -in @("SPE_E5", "ENTERPRISEPREMIUM")) { "Yes" } else { "No" }
                P1               = if ($servicePlansForChecks -contains "AAD_PREMIUM") { "Yes" } else { "No" }
                P2               = if ($servicePlansForChecks -contains "AAD_PREMIUM_P2") { "Yes" } else { "No" }
                DefenderID       = if ($servicePlansForChecks -contains "MDE_ATP") { "Yes" } else { "No" }
                Defender365P1    = if ($servicePlansForChecks -contains "ATP_ENTERPRISE") { "Yes" } else { "No" }
                Defender365P2    = if ($servicePlansForChecks -contains "ATP_ENTERPRISE_PLUS") { "Yes" } else { "No" }
                ServicePlans     = $servicePlanNames
            }
        }

        $date = [datetime]::Now.ToString('yyyyMMddHHmmss')
        $outputFile = Join-Path $OutputDir "$($date)-TenantLicenses.csv"
        $results | Sort-Object -Property Units -Descending | Export-Csv -Path $outputFile -NoTypeInformation -Encoding UTF8
        Write-LogFile -Message "[INFO] License information saved to: $outputFile" -Color "Green" -Level Standard
        Write-LogFile -Message "`nLicense Information:" -Color "Cyan" -Level Standard

        return $results | 
            Sort-Object -Property Units -Descending | 
            Format-Table -Property @(
                @{Label = "License Name"; Expression = {$_.Sku}; Width = 30},
                @{Label = "Status"; Expression = {$_.Status}; Width = 10},
                @{Label = "Units"; Expression = {$_.Units}; Width = 8; Alignment = "Right"},
                @{Label = "Retention"; Expression = {$_.Retention}; Width = 12},
                @{Label = "E3"; Expression = {$_.E3}; Width = 5},
                @{Label = "E5"; Expression = {$_.E5}; Width = 5},
                @{Label = "P1"; Expression = {$_.P1}; Width = 5},
                @{Label = "P2"; Expression = {$_.P2}; Width = 5},
                @{Label = "DefenderID"; Expression = {$_.DefenderID}; Width = 12},
                @{Label = "Def365P1"; Expression = {$_.Defender365P1}; Width = 10},
                @{Label = "Def365P2"; Expression = {$_.Defender365P2}; Width = 10}
            ) -AutoSize


    } catch {
        Write-LogFile -Message "[ERROR] Failed to retrieve licenses: $($_.Exception.Message)" -Color "Red" -Level Minimal
        if ($isDebugEnabled) {
            Write-LogFile -Message "[DEBUG] Error details:" -Level Debug
            Write-LogFile -Message "[DEBUG] Exception type: $($_.Exception.GetType().Name)" -Level Debug
            Write-LogFile -Message "[DEBUG] Stack trace: $($_.ScriptStackTrace)" -Level Debug
        }
        throw
    }
}

Function Get-LicenseCompatibility {
<#
    .SYNOPSIS
    Checks the presence of E5, P2, P1, and E3 licenses and informs about functionality limitations.
 
    .DESCRIPTION
    Determines if E5, P2, P1, and E3 licenses are present and outputs messages regarding the capabilities and limitations.
 
    .PARAMETER OutputDir
    OutputDir is the parameter specifying the output directory.
    Default: Output\Licenses
 
    .PARAMETER LogLevel
    Specifies the level of logging:
    None: No logging
    Minimal: Critical errors only
    Standard: Normal operational logging
    Debug: Verbose logging for debugging purposes
    Default: Standard
 
    .EXAMPLE
    Get-LicenseCompatibility
    Checks for E5, P2, P1, and E3 licenses and outputs corresponding limitations.
#>

    [CmdletBinding()]
    param(
        [ValidateSet('None', 'Minimal', 'Standard', 'Debug')]
        [string]$LogLevel = 'Standard',
        [string]$OutputDir = "Output\Licenses"
    )

    Set-LogLevel -Level ([LogLevel]::$LogLevel)
    $isDebugEnabled = $script:LogLevel -eq [LogLevel]::Debug

    if ($isDebugEnabled) {
        Write-LogFile -Message "[DEBUG] PowerShell Version: $($PSVersionTable.PSVersion)" -Level Debug
        Write-LogFile -Message "[DEBUG] Input parameters:" -Level Debug
        Write-LogFile -Message "[DEBUG] OutputDir: '$OutputDir'" -Level Debug
        Write-LogFile -Message "[DEBUG] LogLevel: '$LogLevel'" -Level Debug
        
        $graphModules = Get-Module -Name Microsoft.Graph* -ErrorAction SilentlyContinue
        if ($graphModules) {
            Write-LogFile -Message "[DEBUG] Microsoft Graph Modules loaded:" -Level Debug
            foreach ($module in $graphModules) {
                Write-LogFile -Message "[DEBUG] - $($module.Name) v$($module.Version)" -Level Debug
            }
        } else {
            Write-LogFile -Message "[DEBUG] No Microsoft Graph modules loaded" -Level Debug
        }
    }

    if (!(Test-Path $OutputDir)) {
        New-Item -ItemType Directory -Force -Path $OutputDir | Out-Null
    }

    Write-LogFile -Message "=== Starting License Compatibility Check ===" -Color "Cyan" -Level Standard

    try {
        $licenses = Get-MgSubscribedSku
        $allServicePlans = $licenses | ForEach-Object { $_.ServicePlans.ServicePlanName }

        if ($isDebugEnabled) {
            Write-LogFile -Message "[DEBUG] Found $($licenses.Count) total SKUs in tenant" -Level Debug
            Write-LogFile -Message "[DEBUG] Total service plans found: $($allServicePlans.Count)" -Level Debug
        }

        $global:e5Present = $licenses | Where-Object { $_.SkuPartNumber -match "E5" }
        $global:e3Present = $licenses | Where-Object { $_.SkuPartNumber -match "E3" }
        $global:p1Present = $allServicePlans -contains "AAD_PREMIUM"
        $global:p2Present = $allServicePlans -contains "AAD_PREMIUM_P2"

        if ($isDebugEnabled) {
            Write-LogFile -Message "[DEBUG] License analysis results:" -Level Debug
            Write-LogFile -Message "[DEBUG] E5 licenses found: $($global:e5Present.Count)" -Level Debug
            if ($global:e5Present) {
                $global:e5Present | ForEach-Object { Write-LogFile -Message "[DEBUG] - $($_.SkuPartNumber): $($_.ConsumedUnits) units" -Level Debug }
            }
            Write-LogFile -Message "[DEBUG] E3 licenses found: $($global:e3Present.Count)" -Level Debug
            if ($global:e3Present) {
                $global:e3Present | ForEach-Object { Write-LogFile -Message "[DEBUG] - $($_.SkuPartNumber): $($_.ConsumedUnits) units" -Level Debug }
            }
            Write-LogFile -Message "[DEBUG] P1 capability present: $global:p1Present" -Level Debug
            Write-LogFile -Message "[DEBUG] P2 capability present: $global:p2Present" -Level Debug
        }

        Write-LogFile -Message "`nLicense Status:" -Color "Cyan" -Level Standard
        Write-LogFile -Message "E5: $(if($global:e5Present){"Present"}else{"Not Present"})" -Color $(if($global:e5Present){"Green"}else{"Yellow"}) -Level Standard
        if (-not $global:e5Present) {
            Write-LogFile -Message "E3: $(if($global:e3Present){"Present"}else{"Not Present"})" -Color $(if($global:e3Present){"Green"}else{"Yellow"}) -Level Standard
        }
        Write-LogFile -Message "P2: $(if($global:p2Present){"Present"}else{"Not Present"})" -Color $(if($global:p2Present){"Green"}else{"Yellow"}) -Level Standard
        if (-not ($global:p2Present -or $global:e5Present)) {
            Write-LogFile -Message "P1: $(if($global:p1Present){"Present"}else{"Not Present"})" -Color $(if($global:p1Present){"Green"}else{"Yellow"}) -Level Standard
        }

        Write-LogFile -Message "`nFeature Compatibility:" -Color "Cyan" -Level Standard

        $features = @(
            @{Feature = "Get-Sessions"; Required = "E5"; Status = $global:e5Present}
            @{Feature = "Get-MessageIDs"; Required = "E5"; Status = $global:e5Present}
            @{Feature = "Get-GraphEntraAuditLogs"; Required = "E5"; Status = $global:e5Present}
            @{Feature = "Get-RiskyUsers"; Required = "E5 or P2"; Status = ($global:e5Present -or $global:p2Present)}
        )

        foreach ($feature in $features) {
            $status = if ($feature.Status) { "Available" } else { "Not Available" }
            $color = if ($feature.Status) { "Green" } else { "Yellow" }
            Write-LogFile -Message "$($feature.Feature) ($($feature.Required)): $status" -Color $color -Level Standard
        }

        Write-LogFile -Message "`nRetention Information:" -Color "Cyan" -Level Standard
        if ($global:e3Present -or $global:e5Present -or $global:p1Present -or $global:p2Present) {
            Write-LogFile -Message "Audit Log retention: 30 days" -Color "Green" -Level Standard
            Write-LogFile -Message "Sign-in Log retention: 30 days" -Color "Green" -Level Standard
        } else {
            Write-LogFile -Message "Audit Log retention: 7 days" -Color "Yellow" -Level Standard
            Write-LogFile -Message "Sign-in Log retention: 7 days" -Color "Yellow" -Level Standard
        }

        $recommendations = @()
        
        if (-not $global:e5Present) {
            $recommendations += "- Consider E5 license for full feature access and extended retention"
        }
        if (-not $global:p2Present -and -not $global:e5Present) {
            $recommendations += "- P2 license would enable risky users monitoring"
        }
        if (-not ($global:e3Present -or $global:e5Present -or $global:p1Present -or $global:p2Present)) {
            $recommendations += "- Current retention period is limited. Consider upgrading for extended retention"
        }

        if ($recommendations.Count -gt 0) {
            Write-LogFile -Message "`nRecommendations:" -Color "Cyan" -Level Standard
            foreach ($recommendation in $recommendations) {
                Write-LogFile -Message $recommendation -Color "Yellow" -Level Standard
            }
        }
    } catch {
        Write-LogFile -Message "[ERROR] Failed to check license capabilities: $($_.Exception.Message)" -Color "Red" -Level Minimal
        if ($isDebugEnabled) {
            Write-LogFile -Message "[DEBUG] Error details:" -Level Debug
            Write-LogFile -Message "[DEBUG] Exception type: $($_.Exception.GetType().Name)" -Level Debug
            Write-LogFile -Message "[DEBUG] Stack trace: $($_.ScriptStackTrace)" -Level Debug
        }
        throw
    }
}     

Function Get-EntraSecurityDefaults {
<#
    .SYNOPSIS
    Checks the status of Entra ID security defaults.
 
    .DESCRIPTION
    Retrieves and logs the status of security defaults and exports the result to a CSV file.
 
    .PARAMETER OutputDir
    OutputDir is the parameter specifying the output directory.
    Default: Output\Licenses
     
    .PARAMETER LogLevel
    Specifies the level of logging:
    None: No logging
    Minimal: Critical errors only
    Standard: Normal operational logging
    Debug: Verbose logging for debugging purposes
    Default: Standard
 
    .EXAMPLE
    Get-EntraSecurityDefaults
    Checks the status of security defaults and saves the results to a CSV file in Output\Licenses.
#>

    [CmdletBinding()]
    param(
        [string]$OutputDir = "Output\Licenses",
        [ValidateSet('None', 'Minimal', 'Standard', 'Debug')]
        [string]$LogLevel = 'Standard'
    )

    Set-LogLevel -Level ([LogLevel]::$LogLevel)
    $isDebugEnabled = $script:LogLevel -eq [LogLevel]::Debug

    if ($isDebugEnabled) {
        Write-LogFile -Message "[DEBUG] PowerShell Version: $($PSVersionTable.PSVersion)" -Level Debug
        Write-LogFile -Message "[DEBUG] Input parameters:" -Level Debug
        Write-LogFile -Message "[DEBUG] OutputDir: '$OutputDir'" -Level Debug
        Write-LogFile -Message "[DEBUG] LogLevel: '$LogLevel'" -Level Debug
        
        $graphModules = Get-Module -Name Microsoft.Graph* -ErrorAction SilentlyContinue
        if ($graphModules) {
            Write-LogFile -Message "[DEBUG] Microsoft Graph Modules loaded:" -Level Debug
            foreach ($module in $graphModules) {
                Write-LogFile -Message "[DEBUG] - $($module.Name) v$($module.Version)" -Level Debug
            }
        } else {
            Write-LogFile -Message "[DEBUG] No Microsoft Graph modules loaded" -Level Debug
        }
    }

    if (!(Test-Path $OutputDir)) {
        New-Item -ItemType Directory -Force -Path $OutputDir | Out-Null
    }

    Write-LogFile -Message "=== Starting Security Defaults Check ===" -Color "Cyan" -Level Standard

    try {
        if ($null -eq $global:e5Present) {
            $licenses = Get-MgSubscribedSku
            $allServicePlans = $licenses | ForEach-Object { $_.ServicePlans.ServicePlanName }
            $global:e5Present = $licenses | Where-Object { $_.SkuPartNumber -match "E5" }
            $global:e3Present = $licenses | Where-Object { $_.SkuPartNumber -match "E3" }
            $global:p2Present = $allServicePlans -contains "AAD_PREMIUM_P2"
            $global:p1Present = $allServicePlans -contains "AAD_PREMIUM"
        }

        $securityDefaults = Get-MgPolicyIdentitySecurityDefaultEnforcementPolicy
        $isEnabled = $securityDefaults.IsEnabled
        $hasPremiumLicense = $global:e5Present -or $global:e3Present -or $global:p2Present -or $global:p1Present


        if ($isDebugEnabled) {
            Write-LogFile -Message "[DEBUG] Security defaults analysis:" -Level Debug
            Write-LogFile -Message "[DEBUG] Security Defaults Enabled: $isEnabled" -Level Debug
            Write-LogFile -Message "[DEBUG] Has Premium License: $hasPremiumLicense" -Level Debug
            Write-LogFile -Message "[DEBUG] Policy ID: $($securityDefaults.Id)" -Level Debug
            Write-LogFile -Message "[DEBUG] Policy Display Name: $($securityDefaults.DisplayName)" -Level Debug
        }

        Write-LogFile -Message "`nSecurity Defaults Status:" -Color "Cyan" -Level Standard
        if ($isEnabled) {
            Write-LogFile -Message "Security Defaults: Enabled" -Color "Green" -Level Standard
        } else {
            Write-LogFile -Message "Security Defaults: Disabled" -Color "Yellow" -Level Standard
        }

        $securityDefaults = Get-MgPolicyIdentitySecurityDefaultEnforcementPolicy
        $isEnabled = $securityDefaults.IsEnabled

        $result = [PSCustomObject]@{
            SecurityDefaultsEnabled = if ($isEnabled) { "Yes" } else { "No" }
        }

        Write-LogFile -Message "`nLicense Context:" -Color "Cyan" -Level Standard
        if ($hasPremiumLicense) {
            Write-LogFile -Message "Premium License(s) Detected:" -Level Standard
            if ($global:e5Present) { Write-LogFile -Message " - E5" -Level Standard }
            if ($global:e3Present -and -not $global:e5Present) { Write-LogFile -Message " - E3" -Level Standard }
            if ($global:p2Present -and -not $global:e5Present) { Write-LogFile -Message " - P2" -Level Standard }
            if ($global:p1Present -and -not ($global:p2Present -or $global:e5Present)) { Write-LogFile -Message " - P1" -Level Standard }
        } else {
            Write-LogFile -Message "No Premium Licenses Detected" -Level Standard
        }

        Write-LogFile -Message "`nRecommendations:" -Color "Cyan" -Level Standard
        if ($hasPremiumLicense) {
            if ($isEnabled) {
                Write-LogFile -Message "[!] With your current license level (Premium), Microsoft recommends:" -Color "Yellow" -Level Standard
                Write-LogFile -Message " - Disable Security Defaults" -Level Standard
                Write-LogFile -Message " - Configure Conditional Access policies for greater control" -Level Standard
                Write-LogFile -Message " - Implement MFA through Conditional Access" -Level Standard
            } else {
                Write-LogFile -Message "Current configuration aligns with Microsoft recommendations" -Color "Green" -Level Standard
                Write-LogFile -Message " - Ensure Conditional Access policies are properly configured" -Level Standard
                Write-LogFile -Message " - Regular review of security policies is recommended" -Level Standard
            }
        } else {
            if ($isEnabled) {
                Write-LogFile -Message "Current configuration aligns with Microsoft recommendations" -Color "Green" -Level Standard
                Write-LogFile -Message " - Security Defaults provide basic security for free/basic licenses" -Level Standard
            } else {
                Write-LogFile -Message "[!] With your current license level (Basic), Microsoft recommends:" -Color "Red" -Level Minimal
                Write-LogFile -Message " - Enable Security Defaults for baseline protection" -Level Standard
                Write-LogFile -Message " - Consider upgrading to premium license for advanced security features" -Level Standard
            }
        }

        $result = [PSCustomObject]@{
            SecurityDefaultsEnabled = if ($isEnabled) { "Yes" } else { "No" }
            HasPremiumLicense = if ($hasPremiumLicense) { "Yes" } else { "No" }
            RecommendedState = if ($hasPremiumLicense) { "Disabled" } else { "Enabled" }
            AlignedWithRecommendations = if (($hasPremiumLicense -and -not $isEnabled) -or (-not $hasPremiumLicense -and $isEnabled)) { "Yes" } else { "No" }
            CheckDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
        }

        $date = [datetime]::Now.ToString('yyyyMMddHHmmss')
        $outputFile = Join-Path $OutputDir "$($date)-EntraSecurityDefaults.csv"
        $result | Export-Csv -Path $outputFile -NoTypeInformation -Encoding UTF8

        Write-LogFile -Message "`nCheck Summary:" -Color "Cyan" -Level Standard
        Write-LogFile -Message ($result | Format-List | Out-String).Trim() -Level Standard
        
        Write-LogFile -Message "`nOutput Files:" -Color "Cyan" -Level Standard
        Write-LogFile -Message "- Results exported to: $outputFile" -Color "Green" -Level Standard
    } catch {
        Write-LogFile -Message "[ERROR] Failed to check security defaults: $($_.Exception.Message)" -Color "Red" -Level Minimal
        if ($isDebugEnabled) {
            Write-LogFile -Message "[DEBUG] Error details:" -Level Debug
            Write-LogFile -Message "[DEBUG] Exception type: $($_.Exception.GetType().Name)" -Level Debug
            Write-LogFile -Message "[DEBUG] Stack trace: $($_.ScriptStackTrace)" -Level Debug
        }
        throw
    }
}

Function Get-LicensesByUser {
<#
    .SYNOPSIS
    Retrieves license assignments for all users in the tenant.
 
    .DESCRIPTION
    Retrieves all licenses assigned to users in the tenant and saves the results to a CSV file.
 
    .PARAMETER OutputDir
    OutputDir is the parameter specifying the output directory.
    Default: Output\Licenses
 
    .PARAMETER LogLevel
    Specifies the level of logging:
    None: No logging
    Minimal: Critical errors only
    Standard: Normal operational logging
    Debug: Verbose logging for debugging purposes
    Default: Standard
 
    .EXAMPLE
    Get-LicensesByUser -OutputDir "Output\Licenses"
    Retrieves license assignments and saves the details to a CSV file in the specified directory.
#>

    [CmdletBinding()]
    param(
        [string]$OutputDir = "Output\Licenses",
        [ValidateSet('None', 'Minimal', 'Standard', 'Debug')]
        [string]$LogLevel = 'Standard'
    )

    Set-LogLevel -Level ([LogLevel]::$LogLevel)
    $isDebugEnabled = $script:LogLevel -eq [LogLevel]::Debug

    if ($isDebugEnabled) {
        Write-LogFile -Message "[DEBUG] PowerShell Version: $($PSVersionTable.PSVersion)" -Level Debug
        Write-LogFile -Message "[DEBUG] Input parameters:" -Level Debug
        Write-LogFile -Message "[DEBUG] OutputDir: '$OutputDir'" -Level Debug
        Write-LogFile -Message "[DEBUG] LogLevel: '$LogLevel'" -Level Debug
        
        $graphModules = Get-Module -Name Microsoft.Graph* -ErrorAction SilentlyContinue
        if ($graphModules) {
            Write-LogFile -Message "[DEBUG] Microsoft Graph Modules loaded:" -Level Debug
            foreach ($module in $graphModules) {
                Write-LogFile -Message "[DEBUG] - $($module.Name) v$($module.Version)" -Level Debug
            }
        } else {
            Write-LogFile -Message "[DEBUG] No Microsoft Graph modules loaded" -Level Debug
        }
    }

    if (!(Test-Path $OutputDir)) {
        New-Item -ItemType Directory -Force -Path $OutputDir | Out-Null
    }

    Write-LogFile -Message "=== Starting User License Collection ===" -Color "Cyan" -Level Standard

    try {
        if (!(Get-MgContext)) {
            Connect-MgGraph -Scopes "User.Read.All", "Directory.Read.All"
        }

        $skus = Get-MgSubscribedSku | Select-Object SkuId, SkuPartNumber
        if ($isDebugEnabled) {
            Write-LogFile -Message "[DEBUG] Retrieved $($skus.Count) SKUs from tenant" -Level Debug
            foreach ($sku in $skus) {
                Write-LogFile -Message "[DEBUG] SKU: $($sku.SkuPartNumber) (ID: $($sku.SkuId))" -Level Debug
            }
        }

        $results = @()
        $users = Get-MgUser -All -Property DisplayName, UserPrincipalName, Id

        if (-not $users) {
            Write-LogFile -Message "[ERROR] No users retrieved. Ensure you have sufficient permissions and users exist in the tenant." -Color "Red" -Level Minimal
            return
        }

        foreach ($user in $users) {
            if (-not $user.Id) {
                Write-LogFile -Message "[ALERT] Skipping user: $($user.DisplayName) - Missing 'Id' property" -Color "Yellow" -Level Standard
                continue
            }

            try {
                $licenseDetails = Get-MgUserLicenseDetail -UserId $user.Id

                if ($licenseDetails) {
                    foreach ($license in $licenseDetails) {
                        $skuPartNumber = ($skus | Where-Object { $_.SkuId -eq $license.SkuId }).SkuPartNumber

                        if ($isDebugEnabled) {
                            Write-LogFile -Message "[DEBUG] Found license: $skuPartNumber for user $($user.UserPrincipalName)" -Level Debug
                        }

                        $results += [PSCustomObject]@{
                            DisplayName       = $user.DisplayName
                            UserPrincipalName = $user.UserPrincipalName
                            SkuPartNumber     = $skuPartNumber
                        }
                    }
                } else {
                    $results += [PSCustomObject]@{
                        DisplayName       = $user.DisplayName
                        UserPrincipalName = $user.UserPrincipalName
                        SkuPartNumber     = "None"
                    }
                }
            } catch {
                Write-LogFile -Message "[ERROR] Failed to retrieve license details for user $($user.UserPrincipalName): $($_.Exception.Message)" -Color "Red" -Level Minimal
                if ($isDebugEnabled) {
                    Write-LogFile -Message "[DEBUG] Error details for user $($user.UserPrincipalName):" -Level Debug
                    Write-LogFile -Message "[DEBUG] Exception type: $($_.Exception.GetType().Name)" -Level Debug
                    Write-LogFile -Message "[DEBUG] Stack trace: $($_.ScriptStackTrace)" -Level Debug
                }
            }
        }

        $date = [datetime]::Now.ToString('yyyyMMddHHmmss')
        $outputFile = Join-Path $OutputDir "$($date)-UserLicenses.csv"
        $results | Sort-Object DisplayName | Export-Csv -Path $outputFile -NoTypeInformation -Encoding UTF8

        $userLicenseSummary = [PSCustomObject]@{
            TotalUsers = ($users | Measure-Object).Count
            LicensedUsers = ($results | Select-Object -Unique UserPrincipalName | Measure-Object).Count
            UnlicensedUsers = (($results | Where-Object { $_.SkuPartNumber -eq "None" }) | Measure-Object).Count
            TotalAssignments = ($results | Where-Object { $_.SkuPartNumber -ne "None" } | Measure-Object).Count
        }

        $licenseDistribution = $results | 
            Where-Object { $_.SkuPartNumber -ne "None" } | 
            Group-Object SkuPartNumber | 
            Sort-Object Count -Descending

        Write-LogFile -Message "`nUser License Summary:" -Color "Cyan" -Level Standard
        Write-LogFile -Message "Total Users: $($userLicenseSummary.TotalUsers)" -Level Standard
        Write-LogFile -Message " - Total License Assignments: $($userLicenseSummary.TotalAssignments)" -Level Standard
        Write-LogFile -Message " - Licensed Users: $($userLicenseSummary.LicensedUsers)" -Level Standard
        Write-LogFile -Message " - Unlicensed Users: $($userLicenseSummary.UnlicensedUsers)" -Level Standard

        Write-LogFile -Message "`nLicense Type Distribution:" -Color "Cyan" -Level Standard
        foreach ($license in $licenseDistribution) {
            Write-LogFile -Message " - $($license.Name): $($license.Count) assignments" -Level Standard
        }

        Write-LogFile -Message "`nExported File:" -Color "Cyan" -Level Standard
        Write-LogFile -Message " - File: $outputFile" -Level Standard
    } catch {
        Write-LogFile -Message "[ERROR] Failed to retrieve user license assignments: $($_.Exception.Message)" -Color "Red" -Level Minimal
        if ($isDebugEnabled) {
            Write-LogFile -Message "[DEBUG] Error details:" -Level Debug
            Write-LogFile -Message "[DEBUG] Exception type: $($_.Exception.GetType().Name)" -Level Debug
            Write-LogFile -Message "[DEBUG] Stack trace: $($_.ScriptStackTrace)" -Level Debug
        }
        throw
    }
}