All scripts
Governance 558

Get-EntraConditionalAccessGapReport

Compares configured Conditional Access policies against a baseline list of expected controls (MFA, legacy auth block, compliant device) and reports which baseline items have no matching policy.

Get-EntraConditionalAccessGapReport.ps1
<#
.SYNOPSIS
    Checks Conditional Access coverage against a baseline control list.

.DESCRIPTION
    Compares the tenant's enabled Conditional Access policies against a
    short baseline of controls every tenant should have (MFA for all
    users, legacy auth blocked, compliant device required for admins)
    and reports which baseline items have no matching enabled policy.

.EXAMPLE
    .\Get-EntraConditionalAccessGapReport.ps1

.NOTES
    Requires Microsoft.Graph.Identity.SignIns with an active
    Connect-MgGraph session.

.AUTHOR
    Shehryar Hassan
#>

$policies = Get-MgIdentityConditionalAccessPolicy | Where-Object { $_.State -eq "enabled" }
$policyNames = $policies.DisplayName -join " | "

$baseline = @(
    @{ Control = "MFA required for all users"; Keyword = "mfa" }
    @{ Control = "Legacy authentication blocked"; Keyword = "legacy" }
    @{ Control = "Compliant device for admins"; Keyword = "admin" }
)

$report = $baseline | ForEach-Object {
    [pscustomobject]@{
        Control = $_.Control
        Covered = $policyNames -match $_.Keyword
    }
}

$report | Format-Table -AutoSize
$report | Where-Object { -not $_.Covered } | ForEach-Object { Write-Warning "No enabled policy found matching: $($_.Control)" }

Read it before you run it, and test in a safe tenant first.