Terraform configuration for managing Microsoft Entra Conditional Access policy baselines with the hashicorp/azuread provider.
These policies are heavily inspired by Joey Verlinden's Conditional Access baseline: https://github.com/j0eyv/ConditionalAccessBaseline.
The repository is organized around policy families:
CA000series: global baseline protectionsCA100series: admin persona protectionsCA200series: internal user protectionsCA300series: service account protectionsCA400series: guest user protections
Most policy resources are currently configured with:
state = "enabledForReportingButNotEnforced"Review sign-in impact before changing policies to enforced mode.
- Terraform
>= 1.15.0 - AzureAD provider
~> 3.8 - Microsoft Entra ID tenant with the licensing required for Conditional Access
- Permissions to create users, groups, named locations, and Conditional Access policies
The AzureAD provider can authenticate using any supported provider method, including Azure CLI, service principal credentials, managed identity, or environment variables.
.
├── main.tf # Calls the Policies child module
├── breakglass.tf # Emergency access users and exclusion group
├── named_locations.tf # Named locations used by location-based policies
├── persona_groups.tf # Persona groups used by policy assignments
├── providers.tf # AzureAD provider configuration
├── terraform.tfvars.example # Example variable values
├── variables.tf # Configurable names, UPNs, rules, and membership
├── versions.tf # Terraform and provider version constraints
├── conversion_warnings.md # Notes for settings not represented in Terraform
└── Policies/ # Conditional Access policy resources
Some policy files are fully commented placeholders. They are retained to preserve the policy sequence and document configurations that are not currently active or not represented by the AzureAD provider.
The framework uses persona groups to avoid hard-coded assignment IDs in Conditional Access policies.
| Persona | Resource | Membership type | Default |
|---|---|---|---|
| Admins | azuread_group.admin_persona |
Dynamic | UPN starts with adm. and ends with .onmicrosoft.com |
| Internals | azuread_group.internals_persona |
Dynamic | user.companyName -eq "DirgoSalga" |
| Service Accounts | azuread_group.service_accounts_persona |
Assigned | Empty member set by default |
Configure these in variables.tf or a local *.tfvars file.
breakglass.tf creates two emergency access users and a role-assignable emergency access exclusion group. The group is excluded from Conditional Access policies.
The default passwords in breakglass.tf are placeholders. Replace or rotate them before using this against a real tenant, and store operational credentials in a vault.
Common variables include:
breakglass_upn_1breakglass_upn_2breakglass_group_nameadmin_persona_group_nameadmin_persona_dynamic_membership_ruleinternals_persona_group_nameinternals_persona_dynamic_membership_ruleservice_accounts_persona_group_nameservice_accounts_persona_member_object_ids
See terraform.tfvars.example for a complete starting point. Example terraform.tfvars:
breakglass_upn_1 = "btg.1@example.com"
breakglass_upn_2 = "btg.2@example.com"
admin_persona_group_name = "CA-Persona-Admins"
internals_persona_group_name = "CA-Persona-Internals"
service_accounts_persona_member_object_ids = [
"00000000-0000-0000-0000-000000000000",
]Do not commit tenant-specific secrets or sensitive operational credentials.
Initialize providers:
terraform initFormat and validate:
terraform fmt -recursive
terraform validateReview planned changes:
terraform planApply after review:
terraform applyReview deployment outputs:
terraform output
terraform output conditional_access_policies
terraform output conditional_access_exclusion_groupsThe outputs include policy IDs, exclusion group object IDs, persona groups, named locations, and emergency access account UPNs. Passwords are intentionally not output.
- Policy naming follows the
CA###-Persona-Control-Apps-Platform-Outcomeconvention. - Every active policy should exclude the break-glass group and its own policy-specific exclusion group.
- Global and persona policies are kept as separate files to make reviews and staged rollouts easier.
conversion_warnings.mdtracks Conditional Access settings that were not represented by the current AzureAD provider, such as Continuous Access Evaluation session controls.
- Keep policies in report-only mode until sign-in logs confirm expected behavior.
- Use Microsoft Entra's What If tool and sign-in logs to test each policy.
- Be careful with
blockcontrols scoped to all resources. - Changes to Terraform resource names can require
movedblocks orterraform state mvif resources already exist in state.
See LICENSE.