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-accessrepository.
- Break-Glass Accounts
- Entra ID Diagnostic Settings: Conditional Access Insights reads the sign-in logs streamed there.
- Microsoft Learn: Conditional Access
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.
- 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.
- Verify break-glass. Sign in with both break-glass accounts in a clean private browser. Keep one session open.
- Disable Security Defaults (Entra ID → Overview → Properties → Manage security defaults).
- Enforce. Set
policy_state = "enabled", then MR, review the plan (in-place updates only,0 to destroy), merge and apply. - Validate. A normal user gets an MFA prompt. A legacy client is blocked. A break-glass account gets straight in.
Lockout recovery¶
- Open a private browser and sign in with a break-glass account.
- Stop the damage, using the fastest option that applies:
- Set the offending policy to Report-only or Off in the portal.
- Or set
policy_stateback to report-only, ordeploy_policies = false, and apply. - As a last resort, remove all policies and re-enable Security Defaults.
- Fix the problem in code and re-apply, then bring state back in line with any portal edits.
- 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.