Files
terraform-cloudflare-tunnel…/Access_Controls-Policies-Rule_Groups.tf
hcornet 4c3150a9b9
Some checks failed
Terraform Apply / Terraform Apply (push) Failing after 4m35s
modify policy
2025-11-24 15:45:52 +01:00

655 lines
20 KiB
HCL

# =============================================================================
# CLOUDFLARE : Access Controls : Policies : Rule Groups
# =============================================================================
locals {
# SAML groups from Okta
saml_groups = {
contractors = "Contractors"
infrastructure_admin = "GL_Users_Infrastructure Admin"
sales_engineering = "GL_Users_Sales Engineering"
sales = "GL_Users_Sales"
it_admin = "GL_Users_IT Admin"
}
# Allowed countries
allowed_countries = ["FR", "DE", "US", "GB"]
blocked_countries = ["CN", "RU", "AF", "BY", "CD", "CU", "IR", "IQ", "KP", "MM", "SD", "SY", "UA", "ZW"]
main_countries = ["FR"]
europe_countries = ["AL","AD","AT","AX","BA","BE","BG","BY","CH","CY","CZ","DE","DK","EE","ES","FI","FR","FO","GB","GG","GI","GR","HR","HU","IE","IM","IS","IT","JE","LI","LT","LU","LV","MC","MD","ME","MK","MT","NL","NO","PL","PT","RO","RS","SE","SI","SK","SM","UA","VA"]
afrique_countries = ["AO","BF","BI","BJ","BW","CD","CF","CG","CI","CM","CV","DJ","DZ","EG","EH","ER","ET","GA","GH","GM","GN","GQ","GW","KE","KM","LR","LS","LY","MA","MG","ML","MR","MU","MW","MZ","NA","NE","NG","RE","RW","SC","SD","SH","SL","SN","SO","SS","ST","SZ","TD","TF","TG","TN","TZ","UG","YT","ZA","ZM","ZW"]
america_north_countries = ["CA","US","MX","BM","PM","GL","UM"]
america_central_countries = ["AG","AI","AW","BB","BZ","CR","CU","DM","DO","GD","GP","GT","HN","HT","JM","KN","KY","LC","MF","MQ","MS","NI","PA","PR","SV","SX","TC","TT","VC","VG","VI"]
america_south_countries = ["AR","BO","BR","CL","CO","EC","FK","GF","GY","PE","PY","SR","UY","VE"]
asie_countries = ["AF","AM","AZ","BD","BH","BN","BT","CN","GE","HK","ID","IL","IN","IQ","IR","JO","JP","KG","KH","KP","KR","KW","KZ","LA","LB","LK","MM","MN","MO","MY","NP","OM","PH","PK","PS","QA","SA","SG","SY","TH","TJ","TL","TM","TR","TW","UZ","VN","YE"]
oceanie_countries = ["AS","AU","CK","FJ","FM","GU","HM","KI","MH","MP","NC","NF","NR","NU","NZ","PF","PG","PN","PW","SB","TK","TO","TV","UM","VU","WF","WS"]
antarctique_countries = ["AQ", "BV"]
other_countries = ["IO","GS"]
# On fusionne *toutes* les zones dans une seule liste
all_countries = flatten([
local.europe_countries,
local.afrique_countries,
local.america_north_countries,
local.america_central_countries,
local.america_south_countries,
local.asie_countries,
local.oceanie_countries,
local.antarctique_countries,
local.other_countries,
])
# On retire les pays "main"
blocked_countries_except_main = compact([
for code in local.all_countries :
(contains(local.main_countries, code) ? null : code)
])
# On retire les pays "europe"
blocked_countries_except_europe = compact([
for code in local.all_countries :
(contains(local.europe_countries, code) ? null : code)
])
# On retire les pays "afrique"
blocked_countries_except_afrique= compact([
for code in local.all_countries :
(contains(local.afrique_countries, code) ? null : code)
])
# On retire les pays "america_north"
blocked_countries_except_america_north= compact([
for code in local.all_countries :
(contains(local.america_north_countries, code) ? null : code)
])
# On retire les pays "america_central"
blocked_countries_except_america_central= compact([
for code in local.all_countries :
(contains(local.america_central_countries, code) ? null : code)
])
# On retire les pays "america_south"
blocked_countries_except_america_south= compact([
for code in local.all_countries :
(contains(local.america_south_countries, code) ? null : code)
])
# On retire les pays "asie"
blocked_countries_except_asie= compact([
for code in local.all_countries :
(contains(local.asie_countries, code) ? null : code)
])
# On retire les pays "oceanie"
blocked_countries_except_oceanie= compact([
for code in local.all_countries :
(contains(local.oceanie_countries, code) ? null : code)
])
# On retire les pays "antarctique"
blocked_countries_except_antarctique= compact([
for code in local.all_countries :
(contains(local.antarctique_countries, code) ? null : code)
])
# On retire les pays "other"
blocked_countries_except_other= compact([
for code in local.all_countries :
(contains(local.other_countries, code) ? null : code)
])
# OS posture checks
os_posture_checks = [
var.cloudflare_linux_posture_id,
var.cloudflare_macos_posture_id,
var.cloudflare_windows_posture_id
]
policy_groups = {
# Composite groups
employees = cloudflare_zero_trust_access_group.employees_rule_group.id
sales_team = cloudflare_zero_trust_access_group.sales_team_rule_group.id
admins = cloudflare_zero_trust_access_group.admins_rule_group.id
contractors = cloudflare_zero_trust_access_group.contractors_rule_group.id
# Individual SAML groups
infrastructure_admin = cloudflare_zero_trust_access_group.saml_groups["infrastructure_admin"].id
sales_engineering = cloudflare_zero_trust_access_group.saml_groups["sales_engineering"].id
sales = cloudflare_zero_trust_access_group.saml_groups["sales"].id
it_admin = cloudflare_zero_trust_access_group.saml_groups["it_admin"].id
}
# Common access policy configurations
access_policies = {
intranet_web_app = {
name = "Intranet App Policy"
include_groups = ["employees", "contractors"]
require_posture = true
require_mfa = false
purpose_justification = false
}
competition_web_app = {
name = "Competition App Policy"
include_groups = ["sales_team"]
require_posture = true
require_mfa = true
# IMPORTANT: Comment out the next 3 lines if you haven't deployed the "Training Compliance Gateway"
# Otherwise the Competition App won't work or show up in App Launcher
# Repository: https://github.com/macharpe/cloudflare-access-training-evaluator
require_external_evaluation = true
external_evaluation_url = "https://training-status.macharpe.com"
external_evaluation_keys_url = "https://training-status.macharpe.com/keys"
purpose_justification = true
purpose_justification_prompt = "Access justification required: Please provide your business reason for accessing this sensitive resource."
lifecycle_create_before_destroy = true
}
aws_employees_browser_rendering = {
name = "Employees AWS Database Policy"
include_groups = ["infrastructure_admin"]
require_posture = true
require_mfa = false
purpose_justification = true
purpose_justification_prompt = "Access justification required: Please provide your business reason for accessing this production system."
require_login_method = true
}
aws_contractors_browser_rendering = {
name = "Contractors AWS Database Policy"
include_groups = ["contractors"]
require_posture = true
require_mfa = false
require_country = true
purpose_justification = true
purpose_justification_prompt = "Access justification required: Please provide your business reason for accessing this production system."
}
gcp_employees_browser_rendering = {
name = "Employees GCP Database Policy"
include_groups = ["infrastructure_admin"]
require_posture = true
require_mfa = false
purpose_justification = true
purpose_justification_prompt = "Access justification required: Please provide your business reason for accessing this production system."
require_login_method = true
}
gcp_contractors_browser_rendering = {
name = "Contractors GCP Database Policy"
include_groups = ["contractors"]
require_posture = true
require_mfa = false
require_country = true
purpose_justification = true
purpose_justification_prompt = "Access justification required: Please provide your business reason for accessing this production system."
}
aws = {
name = "AWS Cloud Policy"
include_groups = ["sales_engineering"]
require_posture = true
require_mfa = true
}
gcp = {
name = "GCP Cloud Policy"
include_groups = ["sales_engineering"]
require_posture = true
require_mfa = true
}
okta = {
name = "Okta Cloud Policy"
include_groups = ["it_admin"]
require_posture = true
require_mfa = true
}
meraki = {
name = "Meraki Cloud Policy"
include_groups = ["it_admin"]
require_posture = true
require_mfa = true
}
domain_controller = {
name = "Domain Controller Policy"
include_groups = ["it_admin", "contractors"]
require_posture = true
require_mfa = true
require_country = true
purpose_justification = true
purpose_justification_prompt = "Access justification required: Please provide your business reason for accessing this sensitive resource."
}
}
}
#==================================================
# Default Rule Groups
#===================================================
resource "cloudflare_zero_trust_access_group" "default_groups" {
account_id = local.cloudflare_account_id
name = "default group"
zone_id = local.cloudflare_zone_id
is_default = true
include = [
{
email = {
email = "thedjinhn@gmail.com"
}
}
]
}
# SAML Rule Groups
resource "cloudflare_zero_trust_access_group" "saml_groups" {
for_each = local.saml_groups
account_id = local.cloudflare_account_id
name = each.value
include = [{
saml = {
identity_provider_id = var.cloudflare_okta_identity_provider_id
attribute_name = "groups"
attribute_value = each.value
}
}]
}
#==================================================
# Composite Rule Groups
#===================================================
resource "cloudflare_zero_trust_access_group" "employees_rule_group" {
account_id = local.cloudflare_account_id
name = "Employees"
include = [
for group_key in ["it_admin", "sales", "sales_engineering", "infrastructure_admin"] : {
group = {
id = cloudflare_zero_trust_access_group.saml_groups[group_key].id
}
}
]
}
resource "cloudflare_zero_trust_access_group" "sales_team_rule_group" {
account_id = local.cloudflare_account_id
name = "Sales Team"
include = [
for group_key in ["sales", "sales_engineering"] : {
group = {
id = cloudflare_zero_trust_access_group.saml_groups[group_key].id
}
}
]
}
resource "cloudflare_zero_trust_access_group" "admins_rule_group" {
account_id = local.cloudflare_account_id
name = "Administrators"
include = [
for group_key in ["it_admin", "infrastructure_admin"] : {
group = {
id = cloudflare_zero_trust_access_group.saml_groups[group_key].id
}
}
]
}
resource "cloudflare_zero_trust_access_group" "contractors_rule_group" {
account_id = local.cloudflare_account_id
name = "Contractors Extended"
include = [
{
group = {
id = cloudflare_zero_trust_access_group.saml_groups["contractors"].id
}
},
{
email_domain = {
domain = var.cloudflare_email_domain
}
}
]
}
# Geographic Rule Groups
resource "cloudflare_zero_trust_access_group" "country_requirements_rule_group" {
account_id = local.cloudflare_account_id
name = "GRP_Localisation_Country Requirements"
include = [
for country in local.allowed_countries : {
geo = {
country_code = country
}
}
]
exclude = [
for country in local.blocked_countries : {
geo = {
country_code = country
}
}
]
}
#
resource "cloudflare_zero_trust_access_group" "country_requirements_rule_group_main" {
account_id = local.cloudflare_account_id
name = "GRP_Localisation Country Requirements : Main"
include = [
for country in local.main_countries : {
geo = {
country_code = country
}
}
]
exclude = [
for country in local.blocked_countries_except_main : {
geo = {
country_code = country
}
}
]
}
#
resource "cloudflare_zero_trust_access_group" "country_requirements_rule_group_europe" {
account_id = local.cloudflare_account_id
name = "GRP_Localisation Country Requirements : Europe"
include = [
for country in local.europe_countries : {
geo = {
country_code = country
}
}
]
exclude = [
for country in local.blocked_countries_except_europe : {
geo = {
country_code = country
}
}
]
}
#
resource "cloudflare_zero_trust_access_group" "country_requirements_rule_group_afrique" {
account_id = local.cloudflare_account_id
name = "GRP_Localisation Country Requirements : Afrique"
include = [
for country in local.afrique_countries : {
geo = {
country_code = country
}
}
]
exclude = [
for country in local.blocked_countries_except_afrique : {
geo = {
country_code = country
}
}
]
}
#
resource "cloudflare_zero_trust_access_group" "country_requirements_rule_group_america_north" {
account_id = local.cloudflare_account_id
name = "GRP_Localisation Country Requirements : America North"
include = [
for country in local.america_north_countries : {
geo = {
country_code = country
}
}
]
exclude = [
for country in local.blocked_countries_except_america_north : {
geo = {
country_code = country
}
}
]
}
#
resource "cloudflare_zero_trust_access_group" "country_requirements_rule_group_america_central" {
account_id = local.cloudflare_account_id
name = "GRP_Localisation Country Requirements : America Central"
include = [
for country in local.america_central_countries : {
geo = {
country_code = country
}
}
]
exclude = [
for country in local.blocked_countries_except_america_central : {
geo = {
country_code = country
}
}
]
}
#
resource "cloudflare_zero_trust_access_group" "country_requirements_rule_group_america_south" {
account_id = local.cloudflare_account_id
name = "GRP_Localisation Country Requirements : America South"
include = [
for country in local.america_south_countries : {
geo = {
country_code = country
}
}
]
exclude = [
for country in local.blocked_countries_except_america_south : {
geo = {
country_code = country
}
}
]
}
#
resource "cloudflare_zero_trust_access_group" "country_requirements_rule_group_asie" {
account_id = local.cloudflare_account_id
name = "GRP_Localisation Country Requirements : Asie"
include = [
for country in local.asie_countries : {
geo = {
country_code = country
}
}
]
exclude = [
for country in local.blocked_countries_except_asie : {
geo = {
country_code = country
}
}
]
}
#
resource "cloudflare_zero_trust_access_group" "country_requirements_rule_group_oceanie" {
account_id = local.cloudflare_account_id
name = "GRP_Localisation Country Requirements : Oceanie"
include = [
for country in local.oceanie_countries : {
geo = {
country_code = country
}
}
]
exclude = [
for country in local.blocked_countries_except_oceanie : {
geo = {
country_code = country
}
}
]
}
#
resource "cloudflare_zero_trust_access_group" "country_requirements_rule_group_antarctique" {
account_id = local.cloudflare_account_id
name = "GRP_Localisation Country Requirements : Antarctique"
include = [
for country in local.antarctique_countries : {
geo = {
country_code = country
}
}
]
exclude = [
for country in local.blocked_countries_except_antarctique : {
geo = {
country_code = country
}
}
]
}
#
resource "cloudflare_zero_trust_access_group" "country_requirements_rule_group_other" {
account_id = local.cloudflare_account_id
name = "GRP_Localisation Country Requirements : other"
include = [
for country in local.other_countries : {
geo = {
country_code = country
}
}
]
exclude = [
for country in local.blocked_countries_except_other : {
geo = {
country_code = country
}
}
]
}
# Device Posture Rule Groups
resource "cloudflare_zero_trust_access_group" "latest_os_version_requirements_rule_group" {
account_id = local.cloudflare_account_id
name = "GL_OS Version Requirements"
include = [
for posture_id in local.os_posture_checks : {
device_posture = {
integration_uid = posture_id
}
}
]
}
#==========================================================
# 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.cloudflare_email_domain
}
}] : []
)
# Require conditions
require = concat(
# Device posture (always required if specified)
try(each.value.require_posture, false) ? [{
device_posture = {
integration_uid = var.cloudflare_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.cloudflare_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
}