⚠️ Status: Beta — core logic is complete, but not yet validated against a live tenant. See Known limitations below.
A PowerShell script that pulls every Conditional Access policy from a Microsoft Entra tenant via Microsoft Graph, resolves the GUIDs to readable names, and generates a Markdown + CSV report — plus flags a handful of common risk patterns automatically (report-only policies, missing grant controls, legacy auth left open, MFA without a sign-in frequency limit, and a few others).
I built this while studying for SC-300 (Identity and Access Administrator). There are already several good CA export scripts out there — this one exists because I wanted something that doesn't just dump the policy list, but actually tells you where to look first.
- Authenticates to Microsoft Graph (client secret or certificate)
- Pulls all Conditional Access policies (
/identity/conditionalAccess/policies) - Resolves included/excluded users, groups, and roles from object IDs to display names
- Runs each policy through a small set of risk checks
- Outputs:
- A Markdown report with a Risk Findings section up top, a summary table, and full per-policy detail
- A flattened CSV for anyone who wants to pivot the data in Excel
| Check | What it catches |
|---|---|
| Report-only state | Policy is configured but not actually enforced |
| Disabled policy | Exists in the tenant but provides zero protection |
| No grant/session controls | Policy that effectively does nothing |
| All users, no MFA, no block | Broad scope with no real teeth |
| Legacy auth targeted but not blocked | Common gap — legacy auth should usually be blocked outright |
| MFA required but no sign-in frequency | Sessions could persist indefinitely once authenticated |
More checks (e.g. comparing two snapshots over time to catch policy drift) are on the roadmap — see Ideas below.
- PowerShell 5.1+ or PowerShell 7+
- An app registration in the target tenant with application permission
Policy.Read.All(andDirectory.Read.Allif you want names instead of GUIDs) - No PowerShell modules required — the script talks to Graph directly over REST, so there's nothing extra to install
# Client secret auth
.\Get-CAPolicyReport.ps1 -TenantId <tenant-guid> -ClientId <app-guid> -ClientSecret <secret> -OutputPath .\reports
# Certificate auth (recommended for anything beyond a quick test)
.\Get-CAPolicyReport.ps1 -TenantId <tenant-guid> -ClientId <app-guid> -CertificateThumbprint <thumbprint>
# Markdown only
.\Get-CAPolicyReport.ps1 -TenantId <tenant-guid> -ClientId <app-guid> -ClientSecret <secret> -Format MarkdownSee examples/sample-report.md for what the output looks
like, run against a small set of fictional policies.
- Entra admin center → App registrations → New registration (single tenant is fine)
- API permissions → Add → Microsoft Graph → Application permissions →
Policy.Read.All(andDirectory.Read.Allif resolving names) - Grant admin consent
- Certificates & secrets → create either a client secret or upload a certificate
- Not yet tested against a production tenant — validated only against synthetic/example data so far
- Certificate auth (JWT signing) has not been exercised end-to-end; the client secret path is the only one tested
- Risk-check heuristics are a first pass — false positives/negatives are possible on complex, multi-condition policies
-CompareTo <previous-report.csv>flag to diff against a prior snapshot and surface what changed (drift detection) — genuinely useful for change-tracking in production- HTML output with collapsible sections instead of a flat Markdown table
- Named locations and authentication strength detail
- Pester tests around the risk-check functions
Built as a portfolio project alongside SC-300 prep — a way to get hands-on with Conditional Access internals and the Graph API beyond what the exam objectives cover. Feedback and PRs welcome.