22. SYNOPSIS
33 Export Conditional Access (CA) policies from Microsoft Entra ID (Azure AD) to a structured CSV file.
44. DESCRIPTION
5- This script connects to Microsoft Graph (Beta) to retrieve Conditional Access policy configurations
6- from Entra ID and exports them to a timestamped CSV file.
7-
5+ This script uses Microsoft Graph (beta) to extract Conditional Access policy configurations into a timestamped CSV report
6+ for audit, compliance, and operational insight.
87 Key Features:
9- • Filters: export only active, disabled, report-only, recently created, or recently modified policies
10- • Authentication: supports both delegated (interactive) and app-based (certificate) authentication
11- • Output: CSV with 30+ attributes; supports exclusion of empty columns
12- • Structure: modular functions, progress indicators, clean output formatting
13- • Hygiene: disconnects from Graph, suppresses output, respects automation use
14- • Standards: compliant with PowerShell 7.2+, Graph SDK, and internal scripting conventions
15-
8+ • Filters: Active, Disabled, Report-Only, recently created or modified
9+ • Output: CSV file with 30+ core CA policy attributes
10+ • Column handling: Optional exclusion of empty columns
11+ • Authentication: Supports interactive and certificate-based Graph auth
12+ • Progress: Includes progress bar with per-policy feedback (safe for divide-by-zero cases)
13+ • Performance: Caches display names and uses optimized object creation
14+ • Reliability: Verifies module presence and avoids redundant imports
15+ • Hygiene: Disconnects from Graph after execution and suppresses disconnect output
16+ • Verbose Mode: Uses [CmdletBinding()] with Write-Verbose for optional detailed output
17+ • Standards: Aligns with PowerShell approved verbs, coding standards, and internal compliance rules
1618. PARAMETER ActiveCAPoliciesOnly
17- Export only policies where State = Enabled.
19+ Only include policies whose State is Enabled.
1820. PARAMETER DisabledCAPoliciesOnly
19- Export only policies where State = Disabled.
21+ Only include policies whose State is Disabled.
2022. PARAMETER ReportOnlyMode
21- Export only policies where State = EnabledForReportingButNotEnforced .
23+ Only include policies in report-only mode .
2224. PARAMETER RecentlyCreatedCAPolicies
23- Export policies created within the last N days.
25+ Include only policies created within the past N days.
2426. PARAMETER RecentlyModifiedCAPolicies
25- Export policies modified within the last N days.
27+ Include only policies modified within the past N days.
2628. PARAMETER CreateSession
27- Disconnect any existing Graph session before connecting .
29+ Force disconnection and re-authentication to Microsoft Graph .
2830. PARAMETER TenantId
29- Azure AD tenant ID (GUID); used for app-only authentication .
31+ Directory ( tenant) ID for Graph auth ( used with ClientId and CertificateThumbprint) .
3032. PARAMETER ClientId
31- Application (client) ID for Graph auth.
33+ Application (client) ID for certificate-based Graph auth.
3234. PARAMETER CertificateThumbprint
33- Certificate thumbprint used for app-only authentication.
35+ Thumbprint of the certificate used for app-only authentication.
3436. PARAMETER OutputDirectory
35- Folder where the CSV file will be saved (default: $PSScriptRoot\Output).
37+ Directory path for the generated CSV file. Default: " $PSScriptRoot\Output"
3638. PARAMETER OutputFileName
37- File name for the exported report (default includes timestamp).
39+ File name for the output. Default: "CA_Policies_Report_< timestamp>.csv"
3840. PARAMETER IncludeEmptyColumns
39- Include columns that have no data in any row.
41+ Switch to include columns that are empty across all results.
42+ . PARAMETER Verbose
43+ Enables detailed console output using Write-Verbose. Available because the script uses [CmdletBinding()].
44+ Use -Verbose to turn on; default is off.
45+ . EXAMPLE
46+ .\Export-EntraCAPoliciesReport.ps1
47+ Exports all CA policies with default settings and minimal console output.
48+ . EXAMPLE
49+ .\Export-EntraCAPoliciesReport.ps1 -ReportOnlyMode -Verbose
50+ Exports only report-only policies and emits detailed progress and status messages.
51+ . EXAMPLE
52+ .\Export-EntraCAPoliciesReport.ps1 -OutputDirectory 'D:\Reports' -OutputFileName 'CA_Policies.csv' -IncludeEmptyColumns
53+ Exports to a custom path and includes columns that are empty across all rows.
4054. NOTES
4155 Author: Travis McDade
4256 Last Updated: 08/08/2025
4559 Author: Kashyap Patel
4660 URL : https://github.com/RapidScripter/export-conditional-access-policies
4761Revision History:
48- 1.0.0 – 08/08/2025 – Finalized for production: cleanup, refactor, export modularization, best practices
62+ 1.0.0 – 08/08/2025 – Production-ready version with CmdletBinding(), Write-Verbose conversion,
63+ module enforcement, property name corrections, improved progress handling,
64+ and Graph session cleanup.
4965 0.4.0 – 08/08/2025 – Refactor for efficiency, object creation, join-logic, header handling
5066 0.3.0 – 08/07/2025 – Column pruning and ordered header logic
5167 0.2.0 – 08/06/2025 – Progress integration and parameter enhancements
@@ -55,6 +71,7 @@ Revision History:
5571# Requires -Version 7.2
5672
5773# region Parameters
74+ [CmdletBinding ()]
5875param
5976(
6077 [switch ]$ActiveCAPoliciesOnly ,
@@ -81,7 +98,7 @@ foreach ($mod in $RequiredModules) {
8198 Write-Host " Module '$mod ' not found. Installing..." - ForegroundColor Yellow
8299 Install-Module - Name $mod - Scope CurrentUser - Force - ErrorAction Stop - Confirm:$false
83100 } else {
84- Write-Host " Module '$mod ' is already installed and available."
101+ Write-Verbose " Module '$mod ' is already installed and available."
85102 }
86103}
87104
@@ -103,9 +120,9 @@ foreach ($sub in $RequiredSubmodules) {
103120# endregion
104121
105122# region Global Hash Caches
106- $global :DirectoryObjsHash = @ {}
107- $global :ServicePrincipalsHash = @ {}
108- $global :NamedLocationHash = @ {}
123+ $script :DirectoryObjsHash = @ {}
124+ $script :ServicePrincipalsHash = @ {}
125+ $script :NamedLocationHash = @ {}
109126
110127# endregion
111128
@@ -116,7 +133,7 @@ function Connect-MgGraphSession {
116133 Disconnect-MgGraph - ErrorAction SilentlyContinue
117134 }
118135
119- Write-Host " Connecting to Microsoft Graph..."
136+ Write-Verbose " Connecting to Microsoft Graph..."
120137
121138 if ($TenantId -and $ClientId -and $CertificateThumbprint ) {
122139 Connect-MgGraph - TenantId $TenantId - AppId $ClientId - CertificateThumbprint $CertificateThumbprint - NoWelcome
@@ -230,7 +247,7 @@ function Export-CaPolicyReport {
230247 }
231248 $Results | Sort-Object ' DisplayName' | Select-Object - Property ($Headers | Where-Object { $nonEmptyProps -contains $_ }) | Export-Csv - Path $Path - NoTypeInformation
232249 } else {
233- $Results | Export-Csv - Path $Path - NoTypeInformation
250+ $Results | Sort-Object ' DisplayName ' | Select-Object - Property $Headers | Export-Csv - Path $Path - NoTypeInformation
234251 }
235252 Write-Progress - Activity " Exporting Conditional Access Policies" - Completed
236253}
@@ -274,7 +291,8 @@ $AllPolicies | ForEach-Object {
274291 $State = $_.State
275292
276293 # Show progress bar for current policy being processed
277- Write-Progress - Activity " Exporting Conditional Access Policies" - Status " Processing: $DisplayName " - PercentComplete (($ProcessedCount / $total ) * 100 )
294+ $percent = if ($total -gt 0 ) { [math ]::Round(($ProcessedCount / $total ) * 100 ) } else { 100 }
295+ Write-Progress - Activity " Exporting Conditional Access Policies" - Status " Processing: $DisplayName " - PercentComplete $percent
278296
279297 # Filter CA policies based on their State
280298 if ($ActiveCAPoliciesOnly.IsPresent -and $State -ne " Enabled" ) {
@@ -369,8 +387,8 @@ $AllPolicies | ForEach-Object {
369387
370388 # --- Conditions Block ---
371389 # Evaluate risk levels, client apps, platforms, and locations
372- $UserRiskLevel = $_.Conditions.UserRiskLevelLevels
373- $SigninRiskLevel = $_.Conditions.SigninRiskLevelLevels
390+ $UserRiskLevel = $_.Conditions.UserRiskLevels
391+ $SigninRiskLevel = $_.Conditions.SignInRiskLevels
374392 $ClientAppTypes = $_.Conditions.ClientAppTypes
375393 $IncludeDevicePlatform = $_.Conditions.Platforms.IncludePlatforms
376394 $ExcludeDevicePlatform = $_.Conditions.Platforms.ExcludePlatforms
@@ -398,7 +416,7 @@ $AllPolicies | ForEach-Object {
398416 # Evaluate grant control settings and operator
399417 $GrantControls = Join-Array $_.GrantControls.BuiltInControls
400418 $GrantControlsOperator = $_.GrantControls.Operator
401- $GrantControlsAuthStrength = $_.GrantControls.GrantControlsAuthStrength .DisplayName
419+ $GrantControlsAuthStrength = $_.GrantControls.AuthenticationStrength .DisplayName
402420
403421 # --- Session Controls Block ---
404422 # Evaluate session controls like app restrictions and sign-in frequency
@@ -462,29 +480,21 @@ $AllPolicies | ForEach-Object {
462480# region Final Output and Export
463481# Define export column order (must match keys in $Result)
464482$orderedHeaders = @ (
465- ' DisplayName' , ' Description' , ' Created Date Time' , ' Modified Date Time' ,
466- ' Include Users' , ' Exclude Users' , ' Include Groups' , ' Exclude Groups' ,
467- ' Include Roles' , ' Exclude Roles' , ' Include Guests or External Users' , ' Exclude Guests or External Users' ,
468- ' Include Applications' , ' Exclude Applications' , ' User Action' , ' User Risk Level' ,
469- ' Signin Risk Level' , ' Client App Types' , ' Include Device Platform' , ' Exclude Device Platform' ,
470- ' Include Locations' , ' Exclude Locations' , ' Grant Controls' , ' Grant Controls Operator' ,
471- ' Grant Controls Authentication Strength' , ' App Enforced Restrictions Enabled' , ' Cloud App Security' ,
472- ' CAE Mode' , ' Disable Resilience Defaults' , ' Signin Frequency Enabled' , ' Signin Frequency Value' ,
473- ' State'
483+ ' DisplayName' , ' Description' , ' State' , ' Include Users' , ' Exclude Users' , ' Include Groups' , ' Exclude Groups' , ' Include Roles' , ' Exclude Roles' , ' Include Guests or External Users' , ' Exclude Guests or External Users' , ' Include Applications' , ' Exclude Applications' , ' User Action' , ' User Risk Level' , ' Signin Risk Level' , ' Client App Types' , ' Include Device Platform' , ' Exclude Device Platform' , ' Include Locations' , ' Exclude Locations' , ' Grant Controls' , ' Grant Controls Operator' , ' Grant Controls Authentication Strength' , ' App Enforced Restrictions Enabled' , ' Cloud App Security' , ' CAE Mode' , ' Disable Resilience Defaults' , ' Signin Frequency Enabled' , ' Signin Frequency Value' , ' Created Date Time' , ' Modified Date Time'
474484)
475485
476486# Finalize and export the filtered policy data to CSV, optionally pruning empty columns
477487if ($Results.Count -eq 0 ) {
478- Write-Host " No data found for the given criteria."
488+ Write-Warning " No data found for the given criteria."
479489} else {
480490 Export-CaPolicyReport - Results $Results - Headers $orderedHeaders - Path $ExportCSV - IncludeEmptyColumns:$IncludeEmptyColumns
481491
482- Write-Host " The output file contains $ ( $Results.Count ) CA policies."
492+ Write-Verbose " The output file contains $ ( $Results.Count ) CA policies."
483493 if ((Test-Path - Path $ExportCSV ) -eq $true ) {
484- Write-Host " The output file is available at: " - ForegroundColor Yellow
494+ Write-Verbose " The output file is available at: $ExportCSV "
485495 Write-Host $ExportCSV
486496 }
487497 # Clean up Microsoft Graph session
488- Write-Host " Disconnecting from Microsoft Graph..."
498+ Write-Verbose " Disconnecting from Microsoft Graph..."
489499 Disconnect-MgGraph - ErrorAction SilentlyContinue | Out-Null
490500}
0 commit comments