Private/Get-NSPConditionalAccessDashboardInsight.ps1

function Get-NSPConditionalAccessDashboardInsight {
    <#
    .SYNOPSIS
        Dashboard: each policy's status in the tenant and the preflight findings, from the saved inventory and profile.
    .DESCRIPTION
        Offline (an unsaved deployment plan and the preflight). The result is kept in -Cache and
        reused until the inventory, the tenant profile, or the ownership record changes, so
        redrawing the dashboard costs nothing.
 
        Each policy row has Id, Name, Family, Status (plain words), Role (a console color role),
        and Action (the plan's). Errors become Problem instead of throwing.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]$State,
        [Parameter(Mandatory)][hashtable]$Cache,
        [string]$ConfigRoot
    )

    if (-not $State.Inventory -or -not $State.ProfilePath) {
        return [pscustomobject]@{ Policies = @(); Findings = @(); Problem = $null; Inventory = $null }
    }
    $ownershipPath = Join-Path (Get-NSPConditionalAccessLocalPath -Kind Ownership -ConfigRoot $ConfigRoot) "$($State.TenantId).json"
    $stamp = @(
        $State.Inventory.FullName, $State.Inventory.LastWriteTimeUtc.Ticks
        (Get-Item -LiteralPath $State.ProfilePath).LastWriteTimeUtc.Ticks
        $(if (Test-Path -LiteralPath $ownershipPath) { (Get-Item -LiteralPath $ownershipPath).LastWriteTimeUtc.Ticks } else { 0 })
    ) -join '|'
    if ($Cache.ContainsKey('Insight') -and $Cache['InsightStamp'] -eq $stamp) { return $Cache['Insight'] }

    $inventory = Get-Content -LiteralPath $State.Inventory.FullName -Raw | ConvertFrom-Json
    $rows = @()
    $findings = @()
    $problem = $null
    try {
        Write-NSPConditionalAccessProgress -Activity Dashboard -Status 'Comparing every template with the inventory' -Step 1 -Of 2
        $plan = New-NSPConditionalAccessDeploymentPlan -Inventory $inventory -TenantProfile $State.Profile -ConfigRoot $ConfigRoot -WhatIf:$false
        $names = @{}
        foreach ($definition in Get-NSPConditionalAccessDefinition) { $names[$definition['Id']] = $definition['Name'] }
        $rows = @($plan.Items | Where-Object { $_.Kind -eq 'Policy' -or $_.Action -eq 'Deselected' } | ForEach-Object {
                $item = $_
                $id = if ($item.DefinitionId) { $item.DefinitionId } else { $item.ItemId }
                $status, $role = switch ($item.Action) {
                    'NoChange' {
                        switch ($item.LiveState) {
                            'enabled' { 'On', 'Success' }
                            'enabledForReportingButNotEnforced' { 'Report-only', 'Heading' }
                            'disabled' { 'Off', 'Muted' }
                            default { [string]$item.LiveState, 'Normal' }
                        }
                    }
                    'Create' { 'Not deployed', 'Muted' }
                    'PendingPrerequisite' { 'Not deployed', 'Muted' }
                    'AdoptOrReview' { 'Found, not adopted', 'Accent' }
                    'UpdateInPlace' { 'Needs update', 'Warning' }
                    'Drifted' { 'Changed by hand', 'Warning' }
                    'Deselected' { 'Replaced (turn off)', 'Warning' }
                    'BlockedPrerequisite' { 'Blocked', 'Error' }
                    'Conflict' { 'Conflict', 'Error' }
                    default { [string]$item.Action, 'Normal' }
                }
                $family = if ($id -match '^NSP-([A-Z0-9]+)-') { $Matches[1] } else { 'OTHER' }
                [pscustomobject]@{
                    Id = $id
                    Name = if ($names.ContainsKey($id)) { $names[$id] } else { [string]$item.DisplayName -replace '^NSP-[A-Z0-9]+(-[A-Z0-9]+)*-\d{2}\s+', '' }
                    Family = $family
                    Status = $status
                    Role = $role
                    Action = $item.Action
                    LiveState = $item.LiveState
                }
            } | Sort-Object Family, Id)
        Write-NSPConditionalAccessProgress -Activity Dashboard -Status 'Lockout and breakglass checks' -Step 2 -Of 2
        $findings = @(Test-NSPConditionalAccessTenant -Inventory $inventory -TenantProfile $State.Profile)
    } catch {
        $problem = "$_"
    }

    Write-NSPConditionalAccessProgress -Activity Dashboard -Completed
    $insight = [pscustomobject]@{ Policies = $rows; Findings = $findings; Problem = $problem; Inventory = $inventory }
    $Cache['Insight'] = $insight
    $Cache['InsightStamp'] = $stamp
    $insight
}