diff --git a/access_groups.tf b/access_groups.tf new file mode 100644 index 0000000..71c6f0f --- /dev/null +++ b/access_groups.tf @@ -0,0 +1,95 @@ +# ============================================================================= +# CLOUDFLARE : Access : Policies +# ============================================================================= + +# +resource "cloudflare_zero_trust_access_policy" "policies" { + for_each = local.access_policies + + account_id = local.cloudflare_account_id + decision = "allow" + name = each.value.name + session_duration = "0s" + + # Purpose justification + purpose_justification_prompt = try(each.value.purpose_justification_prompt, null) + purpose_justification_required = try(each.value.purpose_justification, false) + + # Include groups + include = concat( + # Groups (both SAML and composite groups via mapping) + [ + for group in each.value.include_groups : { + group = { + id = local.policy_groups[group] + } + } + ], + # Email domain (for contractors) + try(each.value.include_email_domain, false) ? [{ + email_domain = { + domain = var.cf_email_domain + } + }] : [] + ) + + # Require conditions + require = concat( + # Device posture (always required if specified) + try(each.value.require_posture, false) ? [{ + device_posture = { + integration_uid = var.cf_gateway_posture_id + } + }] : [], + # MFA requirement + try(each.value.require_mfa, false) ? [{ + auth_method = { + auth_method = "mfa" + } + }] : [], + # Login method (for specific policies) + try(each.value.require_login_method, false) ? [{ + login_method = { + id = var.cf_okta_identity_provider_id + } + }] : [], + # Country requirements + try(each.value.require_country, false) ? [{ + group = { + id = cloudflare_zero_trust_access_group.country_requirements_rule_group.id + } + }] : [], + # OS version requirements + try(each.value.require_os_version, false) ? [{ + group = { + id = cloudflare_zero_trust_access_group.latest_os_version_requirements_rule_group.id + } + }] : [], + # External evaluation requirements + try(each.value.require_external_evaluation, false) ? [{ + external_evaluation = { + evaluate_url = each.value.external_evaluation_url + keys_url = each.value.external_evaluation_keys_url + } + }] : [] + ) + + # Exclude SMS (for MFA policies) + exclude = try(each.value.require_mfa, false) ? [{ + auth_method = { + auth_method = "sms" + } + }] : [] + + # Explicit dependencies to ensure proper destruction order: + # Policies → Composite Groups → Individual SAML Groups + depends_on = [ + cloudflare_zero_trust_access_group.employees_rule_group, + cloudflare_zero_trust_access_group.sales_team_rule_group, + cloudflare_zero_trust_access_group.admins_rule_group, + cloudflare_zero_trust_access_group.contractors_rule_group, + cloudflare_zero_trust_access_group.saml_groups + ] + + # Note: lifecycle blocks cannot be conditional in for_each resources +} \ No newline at end of file