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