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
    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')]
        [string]$LogLevel = 'Standard'
    )

    Set-LogLevel -Level ([LogLevel]::$LogLevel)

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

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

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

        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

            [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
        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 LogLevel
    Specifies the level of logging:
    None: No logging
    Minimal: Critical errors only
    Standard: Normal operational logging
    Default: Standard
 
    .EXAMPLE
    Get-LicenseCompatibility
    Checks for E5, P2, P1, and E3 licenses and outputs corresponding limitations.
#>

    [CmdletBinding()]
    param(
        [ValidateSet('None', 'Minimal', 'Standard')]
        [string]$LogLevel = 'Standard'
    )

    Set-LogLevel -Level ([LogLevel]::$LogLevel)
    Write-LogFile -Message "=== Starting License Compatibility Check ===" -Color "Cyan" -Level Minimal

    try {
        $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:p1Present = $allServicePlans -contains "AAD_PREMIUM"
        $global:p2Present = $allServicePlans -contains "AAD_PREMIUM_P2"


        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 Minima
        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
    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')]
        [string]$LogLevel = 'Standard'
    )

    Set-LogLevel -Level ([LogLevel]::$LogLevel)

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

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

    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

        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
        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
    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')]
        [string]$LogLevel = 'Standard'
    )

    Set-LogLevel -Level ([LogLevel]::$LogLevel)

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

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

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

        $skus = Get-MgSubscribedSku | Select-Object SkuId, SkuPartNumber
        $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

                        $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
            }
        }

        $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
        throw
    }
}