Skip to content

Conditional Access

Conditional Access policies decide who can sign in, from where, with what, and how often they must prove it again. They are evaluated on every sign-in in the tenant. That makes them the strongest identity control, and the easiest way to lock everyone out. The whole policy set is managed as code in the azure-conditional-access repository.

Status: deployed in report-only mode

All 16 policies exist. They run as enabledForReportingButNotEnforced: they log what they would do and enforce nothing. Security Defaults still provides the baseline MFA today. Cutting over to enforcement is planned.


Safety guardrails

Guardrail What it does
Break-glass exclusion Every policy excludes grp-identity-emergency-access. This is enforced in code through local.break_glass_exclusion, and a policy without it must not be merged.
policy_state One switch for all policies: report-only, enabled or disabled
deploy_policies Master switch. false removes every policy.
Per-family enable_* flags Keeps policies that need a prerequisite, such as Intune or a reviewed country list, switched off until it's ready

The policy set

Display names follow CANNN | <who> | <what / where> | <control>, because Entra has no description field for Conditional Access policies.

Policy Control Prerequisite On by default
CA001 All users: require MFA — Yes
CA002 Block legacy authentication — Yes
CA003 Admin roles: require MFA — Yes
CA004 Azure management (portal, CLI, ARM): require MFA — Yes
CA005 Guests and external users: require MFA — Yes
CA006 Device registration: require MFA — Yes
CA007 Medium or high sign-in risk: require MFA Entra ID P2 Yes
CA008 High user risk: require MFA and a password change Entra ID P2 Yes
CA009 Admin roles: require phishing-resistant MFA — Yes
CA010 Admin roles: 12-hour sign-in frequency, no persistent browser — Yes
CA011 All users: block persistent browser sessions — No
CA012 Admin roles: require a compliant or hybrid-joined device Intune No
CA013 All users: require a compliant or hybrid-joined device Intune No
CA014 Block sign-in from sanctioned countries Reviewed country list Yes
CA015 Block sign-in from outside operating countries Reviewed country list Yes
CA016 Block unsupported device platforms — No

CA014 (a block list) and CA015 (an allow list) run side by side on purpose. Compare their report-only results, then keep the one that fits.

Why policies are written by hand

Each policy mirrors a Microsoft Conditional Access template, but is defined in Terraform. Microsoft-managed policies are not used. Microsoft edits them on its own schedule, promotes them from report-only to enforced after a grace period, and they can't be deleted, which conflicts with managing everything as code.


Cutover to enforcement

Follow the steps in order. Between steps 3 and 4 the tenant briefly has no MFA baseline.

  1. Soak. Leave report-only for days. In Sign-in logs → Conditional Access → Report-only, look for anything that would have been blocked but shouldn't be: automation, mail clients, partners, travelling staff.
  2. Verify break-glass. Sign in with both break-glass accounts in a clean private browser. Keep one session open.
  3. Disable Security Defaults (Entra ID → Overview → Properties → Manage security defaults).
  4. Enforce. Set policy_state = "enabled", then MR, review the plan (in-place updates only, 0 to destroy), merge and apply.
  5. Validate. A normal user gets an MFA prompt. A legacy client is blocked. A break-glass account gets straight in.

Lockout recovery

  1. Open a private browser and sign in with a break-glass account.
  2. Stop the damage, using the fastest option that applies:
    • Set the offending policy to Report-only or Off in the portal.
    • Or set policy_state back to report-only, or deploy_policies = false, and apply.
    • As a last resort, remove all policies and re-enable Security Defaults.
  3. Fix the problem in code and re-apply, then bring state back in line with any portal edits.
  4. Rotate the break-glass credentials if anyone outside the safe process saw them.

Pipeline identity

sp-pla-tf-conditional-access-rw holds Graph application permissions: Policy.ReadWrite.ConditionalAccess (the only write permission), plus Policy.Read.All, Group.Read.All, User.Read.All and Application.Read.All. It has no permission to change group membership or roles, so it cannot escalate privilege. The repository applies from CI after a manual approval job.

Tier classification

It can't grant privilege, but an identity that can rewrite every Conditional Access policy can weaken or remove sign-in protection for the whole tenant. That is control-plane power, and it is applied from CI. This is recorded as EX-006 in the Exceptions Register.