Public/Invoke-AuthContextInventoryReport.ps1

function Invoke-AuthContextInventoryReport {
    <#
    .SYNOPSIS
    Comprehensive inventory of Authentication Context enforcement across Microsoft 365, Entra ID, and Azure environments.
 
    .DESCRIPTION
    Performs a phased, read-only discovery of Authentication Context enforcement across:
     
    Phase 1 - Purview & Exchange Online:
        * Sensitivity labels (Sites & Groups scope) with embedded Authentication Context requirements
        * Exchange Online protection policies and protected actions
     
    Phase 2 - Microsoft Graph:
        * Authentication Context Class References discovery
        * M365 Groups / Teams with Authentication Context enforcement via labels
        * Conditional Access policies referencing Authentication Contexts
        * Entra PIM (directory + group roles) requiring Authentication Context for activation
     
    Phase 3 - SharePoint Online:
        * SharePoint sites with direct Authentication Context assignment
        * Site-level Authentication Context policies and enforcement
     
    Phase 4 - Azure Resources (Optional):
        * Azure Resource PIM policies requiring Authentication Context for role activation
        * Subscription and resource group level PIM policy analysis
 
    .PARAMETER TenantName
    Tenant short name (e.g. 'contoso' from https://contoso-admin.sharepoint.com).
    If not specified, will attempt to auto-detect from current context.
 
    .PARAMETER OutputPath
    Directory for report output. Default: C:\Reports\M365AuthContext\
 
    .PARAMETER UserPrincipalName
    Optional UPN hint to reduce interactive authentication prompts across Purview, Graph, and Azure connections.
 
    .PARAMETER SharePointOnlineCredential
    Optional credential for SharePoint Online authentication (break-glass scenarios only).
 
    .PARAMETER Quiet
    Minimize console output to essential status messages only.
 
    .PARAMETER NoProgress
    Suppress Write-Progress updates during processing.
 
    .PARAMETER HtmlReportPath
    Custom path for HTML report output. If not specified, uses OutputPath with auto-generated filename.
 
    .PARAMETER NoAutoOpen
    Prevent automatic opening of the HTML report after generation.
 
    .PARAMETER ExcludeAzure
    Skip Azure Resource PIM enumeration entirely. Reduces required permissions and module dependencies.
 
    .PARAMETER AzureSubscriptionIds
    Specific Azure subscription IDs to process for PIM policies. Accepts single ID or array of subscription GUIDs.
    If not specified, all accessible subscriptions are processed. Use with -ExcludeAzure to skip Azure entirely.
 
    .PARAMETER HtmlStyle
    Select the visual theme for the HTML report. Available values: Classic, Dark.
    Classic is the light theme. Dark is the dark theme. A runtime toggle button in the report lets you switch between them regardless of initial choice.
 
    .PARAMETER HtmlLayout
    Structural layout variant for the report content: TabbedOverview (default - Overview tab plus individual section tabs), Classic (sections stacked), Tabbed, Sidebar (navigation rail), Masonry (multi-column cards), Dashboard (summary cards with modal detail), Layoutv2 (modern card grid with search & collapsible cards).
 
    .PARAMETER HtmlAllLayoutThemes
    When specified, generate all theme variants for the chosen layout alongside the primary report.
 
    .PARAMETER HtmlAllLayouts
    Generate all layout variants (Classic, Tabbed, Sidebar, Masonry, Dashboard) for the chosen theme. Can be combined with -HtmlAllLayoutThemes to produce every style/layout permutation.
 
    .OUTPUTS
    HTML report consolidating Authentication Context usage across all discovered services and policies.
    Returns the path to the generated HTML report.
 
    .EXAMPLE
    Invoke-AuthContextInventoryReport -TenantName contoso
     
    Performs full discovery across all phases and generates comprehensive HTML report.
 
    .EXAMPLE
    Invoke-AuthContextInventoryReport -TenantName contoso -ExcludeAzure -Quiet
     
    Skips Azure Resource PIM phase and minimizes console output. Useful for environments without Azure access.
 
    .EXAMPLE
    Invoke-AuthContextInventoryReport -TenantName contoso -AzureSubscriptionIds @('12345678-1234-1234-1234-123456789abc','87654321-4321-4321-4321-987654321def')
     
    Processes only specified Azure subscriptions for PIM policy analysis, reducing scope and processing time.
 
    .EXAMPLE
    Invoke-AuthContextInventoryReport -TenantName contoso -UserPrincipalName admin@contoso.com -OutputPath D:\Reports -NoAutoOpen
     
    Uses specified UPN for authentication hints, custom output directory, and prevents automatic report opening.
 
    .NOTES
    Module: M365IdentityPosture
    Author: Sebastian Flæng Markdanner
    Website: https://chanceofsecurity.com
    Version: 1.0.0
    Date: 07-10-2025
     
    All operations are read-only; no configuration changes performed.
 
    Required Permissions:
 
    Microsoft Graph API (Delegated):
    - Directory.Read.All # Read directory data, users, groups
    - Group.Read.All # Read all groups and their properties
    - Policy.Read.All # Read all organizational policies
    - Policy.Read.ConditionalAccess # Read Conditional Access policies
    - AuthenticationContext.Read.All # Read authentication context classes
    - RoleManagement.Read.Directory # Read directory role assignments
    - RoleManagement.Read.All # Read all role management data
    - PrivilegedAccess.Read.AzureADGroup # Read PIM group policies
    - InformationProtectionPolicy.Read.All # Read sensitivity labels
    - User.Read.All # Read user profiles
    - Application.Read.All # Read application registrations
    - AuditLog.Read.All # Read audit logs (for PIM policies)
    - CrossTenantInformation.ReadBasic.All # Read tenant information
    - Sites.Read.All # Read SharePoint sites via Graph
     
    Exchange Online Management:
    - View-Only Organization Management role # Read Exchange configuration
    - Or Global Reader role # Alternative read-only access
     
    SharePoint Online:
    - SharePoint Administrator role # Full SharePoint read access
    - Or Global Reader role # Alternative read-only access
     
    Azure (Optional - for Azure Resource PIM):
    - Reader role on target subscriptions # Read Azure resource configuration
    - Security Reader role # Read security configurations
    #>

    [CmdletBinding()]
    param(
        [Parameter()]
        [Alias('Tenant')]
        [ValidatePattern('^[a-z0-9-]+$')]
        [string]$TenantName,
    
        [Parameter()]
        [Alias('OutPath')]
        [ValidateScript({
                if (!(Test-Path $_)) {
                    try {
                        New-Item -ItemType Directory -Force -Path $_ | Out-Null
                        return $true
                    }
                    catch {
                        throw "Cannot create output directory: $_"
                    }
                }
                return $true
            })]
        [string]$OutputPath = 'C:\Reports\M365AuthContext\',
    
        [Parameter()]
        [Alias('UPN')]
        [ValidatePattern('^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$')]
        [string]$UserPrincipalName,
    
        [Parameter()]
        [Alias('Credential')]
        [System.Management.Automation.PSCredential]$SharePointOnlineCredential,
    
        [Parameter()]
        [Alias('Silent')]
        [switch]$Quiet,
    
        [Parameter()]
        [switch]$NoProgress,
    
        [Parameter()]
        [Alias('HtmlPath')]
        [ValidatePattern('\.html?$')]
        [string]$HtmlReportPath,
    
        [Parameter()]
        [Alias('DoNotOpen')]
        [switch]$NoAutoOpen,
    
        [Parameter()]
        [Alias('NoAzurePIM')]
        [switch]$ExcludeAzure,
    
        [Parameter()]
        [Alias('SubscriptionIds', 'SubIds')]
        [ValidateScript({
                foreach ($id in $_) {
                    if ($id -notmatch '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$') {
                        throw "Invalid subscription ID format: $id"
                    }
                }
                return $true
            })]
        [string[]]$AzureSubscriptionIds
        ,
        [Parameter()]
        [ValidateSet('Classic', 'Dark')]
        [string]$HtmlStyle = 'Classic'
        ,
        [Parameter()]
        [ValidateSet('TabbedOverview', 'Classic', 'Tabbed', 'Sidebar', 'Masonry', 'Dashboard', 'Layoutv2')]
        [string]$HtmlLayout = 'TabbedOverview'
        ,
        [Parameter()]
        [switch]$HtmlAllLayoutThemes
        ,
        [Parameter()]
        [switch]$HtmlAllLayouts
    )

    begin {
        # Ensure module state is properly initialized
        if (-not $script:ModuleVersion) {
            Write-Warning 'Module state not initialized. Some features may not work correctly.'
        }
    
        # Log the start of the operation
        Write-ModuleLog -Message 'Starting Authentication Context Inventory Report' -Level Info
        Write-ModuleLog -Message "Parameters: TenantName=$TenantName, OutputPath=$OutputPath, ExcludeAzure=$ExcludeAzure" -Level Debug
    
        # Set script-level variables for use in nested functions
        $script:ReportQuiet = $Quiet
        $script:ReportNoProgress = $NoProgress
    }

    process {
        try {
    
            # Call the core inventory function
            $reportParams = @{
                Quiet        = $Quiet
                NoProgress   = $NoProgress
                OutputPath   = $OutputPath
                ExcludeAzure = $ExcludeAzure
            }
        
            # Add optional parameters only if provided
            if ($TenantName) { $reportParams['TenantName'] = $TenantName }
            if ($UserPrincipalName) { $reportParams['UserPrincipalName'] = $UserPrincipalName }
            if ($SharePointOnlineCredential) { $reportParams['SharePointOnlineCredential'] = $SharePointOnlineCredential }
            if ($HtmlReportPath) { $reportParams['HtmlReportPath'] = $HtmlReportPath }
            if ($NoAutoOpen) { $reportParams['NoAutoOpen'] = $NoAutoOpen }
            if ($AzureSubscriptionIds) { $reportParams['AzureSubscriptionIds'] = $AzureSubscriptionIds }
            if ($HtmlStyle) { $reportParams['HtmlStyle'] = $HtmlStyle }
            if ($HtmlLayout) { $reportParams['HtmlLayout'] = $HtmlLayout }
            if ($HtmlAllLayoutThemes) { $reportParams['HtmlAllLayoutThemes'] = $HtmlAllLayoutThemes }
            if ($HtmlAllLayouts) { $reportParams['HtmlAllLayouts'] = $HtmlAllLayouts }
        
            # Invoke the core function
            $result = Invoke-AuthContextInventoryCore @reportParams
        
            # Return the report path
            return $result
        }
        catch {
            Write-ModuleLog -Message "Error during report generation: $_" -Level Error
            throw
        }
    }

    end {
        Write-ModuleLog -Message 'Authentication Context Inventory Report completed' -Level Info
    
        if (-not $Quiet) {
            Write-Host "`n═══════════════════════════════════════════════════════════════" -ForegroundColor Cyan
            Write-Host ' Report Generation Complete' -ForegroundColor Green
            Write-Host " Log file: $script:LogPath" -ForegroundColor Gray
            Write-Host "═══════════════════════════════════════════════════════════════`n" -ForegroundColor Cyan
        }
    }
}