The perimeter-based security model is dead. In today’s cloud-first world, identity is the new control plane. To achieve a true Zero Trust Conditional Access architecture, organizations rely heavily on Microsoft Entra ID policies to evaluate every sign-in attempt based on identity, device compliance, location, and risk.
However, manually managing these policies across multiple tenants—or even within a single large enterprise—is prone to human error. A single misconfigured policy can either lock out your entire admin team or expose your tenant to attacks.
By leveraging Conditional Access as Code using the Microsoft Graph PowerShell SDK, you can automate Conditional Access Backup, standardize Entra ID Security Baseline deployments, and enforce version control.
Note on Terminology
All examples in this article use Microsoft Entra ID terminology. The legacy Azure AD PowerShell and AzureAD modules are deprecated. Microsoft recommends using the Graph PowerShell Conditional Access commands for all new automation projects.
The Conditional Access Policy Lifecycle
Automated security isn’t just about deploying a script; it is about establishing a continuous, controlled lifecycle for your policies:
Prerequisites: Graph API Permissions
To read and write Conditional Access policies, you need elevated permissions in Microsoft Graph. If you are running these scripts as a Service Principal or Managed Identity, ensure you grant the correct application permissions.
# Connect with necessary scopes for CA management
Connect-MgGraph -Scopes `
"Policy.ReadWrite.ConditionalAccess",
"Policy.Read.All",
"Application.Read.All" -Interactive
Step 1: Conditional Access JSON Export (Backup Automation)
Before you make any changes to your tenant, you must have a backup. Conditional Access policies cannot be restored easily from a recycle bin. If someone deletes or modifies a critical policy, you need the JSON payload to recreate it.
The following script queries all policies and exports them as formatted JSON files. Notice we use the -All flag to handle API pagination and -Depth 30 to capture deeply nested objects like Authentication Contexts and Device Filters.
$BackupDir = "C:\M365_Backups\ConditionalAccess"
if (-not (Test-Path $BackupDir)) { New-Item -ItemType Directory -Path $BackupDir }
# Retrieve all CA Policies
$Policies = Get-MgIdentityConditionalAccessPolicy -All
Write-Host "Found $($Policies.Count) policies. Exporting..." -ForegroundColor Cyan
foreach ($Policy in $Policies) {
# Clean the display name for a safe file name
$SafeName = $Policy.DisplayName -replace '[^a-zA-Z0-9]', '_'
$FilePath = Join-Path -Path $BackupDir -ChildPath "CAPolicy_$($SafeName).json"
# Convert policy object to JSON with sufficient depth to avoid data loss
$JsonPayload = $Policy | ConvertTo-Json -Depth 30
$JsonPayload | Out-File -FilePath $FilePath -Encoding UTF8
Write-Host "Exported: $SafeName" -ForegroundColor Green
}
Step 2: Deploying a CA Baseline Policy
Once you have a standard JSON payload, you can deploy it to any tenant automatically. A classic example is blocking legacy protocols.
Legacy authentication protocols such as POP3, IMAP4, Exchange ActiveSync, MAPI over HTTP, Remote PowerShell, and other protocols that do not support modern authentication are common targets for password spray and credential stuffing attacks.
Critical Rule: The Break-Glass Account
Never deploy a Conditional Access policy without excluding at least one highly secure “Break-Glass” Global Admin account. If a policy is misconfigured, this excluded account is your only way back into the tenant.
$JsonPath = "C:\Baselines\Block_Legacy_Authentication.json"
$PolicyRaw = Get-Content -Path $JsonPath -Raw
$PolicyObj = $PolicyRaw | ConvertFrom-Json
# Construct the body parameter using the imported JSON structure
$NewPolicyParams = @{
DisplayName = "[Auto-Baseline] $($PolicyObj.DisplayName)"
# ALWAYS use Report-Only for new deployments
State = "enabledForReportingButNotEnforced"
Conditions = $PolicyObj.Conditions
GrantControls = $PolicyObj.GrantControls
SessionControls = $PolicyObj.SessionControls
}
try {
$Result = New-MgIdentityConditionalAccessPolicy -BodyParameter $NewPolicyParams
Write-Host "Successfully deployed: $($Result.DisplayName)" -ForegroundColor Green
} catch {
Write-Host "Failed to deploy policy: $_" -ForegroundColor Red
}
Why Report-Only?
Review Report-Only results long enough to cover normal user access patterns, including weekends, remote access, and business-critical workflows before enabling enforcement.
Step 3: Restoring a Conditional Access Policy
In the event of accidental deletion or misconfiguration, the exported JSON can be used to recreate the policy. By reading the JSON file back into PowerShell and mapping it to the New-MgIdentityConditionalAccessPolicy cmdlet, you can instantly restore your tenant’s security posture to a known good state.
Modern Recommendation: Authentication Strength
Instead of relying solely on a simple “Require MFA” grant control, modern Conditional Access deployments should enforce Authentication Strength to combat AiTM (Adversary-in-the-Middle) and MFA fatigue attacks.
Consider using Authentication Strength policies to enforce phishing-resistant methods such as:
- FIDO2 Security Keys
- Windows Hello for Business
- Certificate-Based Authentication (CBA)
Advanced Controls: Devices and Named Locations
Modern Microsoft Entra ID Automation frequently leverages dynamic conditions beyond just identity and risk. These controls offer stronger security than traditional IP-based restrictions:
- Require compliant device: Ensure the device is Intune-managed and passes compliance checks.
- Filter for devices: Target specific hardware or operating systems.
- Sign-in Risk & User Risk: Dynamically step up authentication based on Microsoft’s threat intelligence.
Before deploying location-based policies, maintain Named Locations for your corporate offices, trusted VPN ranges, business partners, and temporary exception networks to avoid inadvertently blocking legitimate traffic.
Conclusion
Automating your Microsoft Entra ID Automation with the Graph PowerShell SDK is a cornerstone of a mature security strategy. By treating your policies as code, you eliminate configuration drift and enable rapid recovery from unauthorized changes.
However, securing the front door is only half the battle. Stale, unmanaged, or orphaned accounts bypassing MFA policies remain a massive blind spot. To ensure your tenant stays perfectly hygienic, consider implementing lifecycle automation with SPClean. SPClean automatically identifies and remediates dormant accounts and abandoned SharePoint resources, reducing your attack surface and keeping your Zero-Trust architecture airtight.
FAQ
Can Conditional Access policies be backed up automatically?
Yes, by using Get-MgIdentityConditionalAccessPolicy -All in an Azure Automation Runbook (with a Managed Identity), you can schedule nightly exports to JSON and commit them directly to a Git repository.
Can I deploy Conditional Access policies across multiple tenants?
Absolutely. The “Conditional Access as Code” approach allows you to export a JSON baseline from a golden tenant and import it across multiple environments using Graph PowerShell, making it ideal for MSPs.
What is the safest way to enable a new Conditional Access policy?
Always deploy policies with the state set to enabledForReportingButNotEnforced. Monitor the Entra ID Sign-in logs for a sufficient period (including weekends) to catch any false positives or broken workflows before flipping the policy to enforced.



