diff --git a/modules/vpc-sc/README.md b/modules/vpc-sc/README.md index 8436eaf7e..e78dce364 100644 --- a/modules/vpc-sc/README.md +++ b/modules/vpc-sc/README.md @@ -410,7 +410,7 @@ to: | [iam_bindings](variables.tf#L149) | Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary. | map(object({…})) | | {} | | [iam_bindings_additive](variables.tf#L164) | Individual additive IAM bindings. Keys are arbitrary. | map(object({…})) | | {} | | [ingress_policies](variables.tf#L179) | Ingress policy definitions that can be referenced in perimeters. | map(object({…})) | | {} | -| [perimeters](variables.tf#L221) | Regular service perimeters. | map(object({…})) | | {} | +| [perimeters](variables.tf#L221) | Regular service perimeters. | map(object({…})) | | {} | | [project_id_search_scope](variables.tf#L255) | Set this to an organization or folder ID to use Cloud Asset Inventory to automatically translate project ids to numbers. | string | | null | ## Outputs diff --git a/modules/vpc-sc/perimeters-additive.tf b/modules/vpc-sc/perimeters-additive.tf index bc562b938..04c63fb3a 100644 --- a/modules/vpc-sc/perimeters-additive.tf +++ b/modules/vpc-sc/perimeters-additive.tf @@ -48,8 +48,8 @@ resource "google_access_context_manager_service_perimeter" "additive" { ]) dynamic "egress_policies" { - for_each = spec.value.egress_policies == null ? [] : [ - for k in spec.value.egress_policies : + for_each = [ + for k in coalesce(spec.value.egress_policies, []) : merge(local.egress_policies[k], { key = k }) ] iterator = policy @@ -129,8 +129,8 @@ resource "google_access_context_manager_service_perimeter" "additive" { } dynamic "ingress_policies" { - for_each = spec.value.ingress_policies == null ? [] : [ - for k in spec.value.ingress_policies : + for_each = [ + for k in coalesce(spec.value.ingress_policies, []) : merge(local.ingress_policies[k], { key = k }) ] iterator = policy @@ -235,8 +235,8 @@ resource "google_access_context_manager_service_perimeter" "additive" { ]) dynamic "egress_policies" { - for_each = status.value.egress_policies == null ? [] : [ - for k in status.value.egress_policies : + for_each = [ + for k in coalesce(status.value.egress_policies, []) : merge(local.egress_policies[k], { key = k }) ] iterator = policy @@ -310,8 +310,8 @@ resource "google_access_context_manager_service_perimeter" "additive" { } dynamic "ingress_policies" { - for_each = status.value.ingress_policies == null ? [] : [ - for k in status.value.ingress_policies : + for_each = [ + for k in coalesce(status.value.ingress_policies, []) : merge(local.ingress_policies[k], { key = k }) ] iterator = policy @@ -397,6 +397,14 @@ resource "google_access_context_manager_service_perimeter" "additive" { } lifecycle { ignore_changes = [spec[0].resources, status[0].resources] + precondition { + condition = length(local._undefined_ingress_policies[each.key]) == 0 + error_message = "Undefined ingress policies: ${join(", ", local._undefined_ingress_policies[each.key])}" + } + precondition { + condition = length(local._undefined_egress_policies[each.key]) == 0 + error_message = "Undefined egress policies: ${join(", ", local._undefined_egress_policies[each.key])}" + } } depends_on = [ google_access_context_manager_access_policy.default, diff --git a/modules/vpc-sc/perimeters.tf b/modules/vpc-sc/perimeters.tf index ecf92fc33..6fdb11175 100644 --- a/modules/vpc-sc/perimeters.tf +++ b/modules/vpc-sc/perimeters.tf @@ -20,6 +20,14 @@ locals { egress_policies = merge(local.data.egress_policies, var.egress_policies) ingress_policies = merge(local.data.ingress_policies, var.ingress_policies) perimeters = merge(local.data.perimeters, var.perimeters) + _undefined_egress_policies = { + for k, v in local.perimeters : + k => setsubtract(concat(try(v.spec.egress_policies, []), try(v.status.egress_policies, [])), keys(local.egress_policies)) + } + _undefined_ingress_policies = { + for k, v in local.perimeters : + k => setsubtract(concat(try(v.spec.ingress_policies, []), try(v.status.ingress_policies, [])), keys(local.ingress_policies)) + } } resource "google_access_context_manager_service_perimeter" "regular" { @@ -54,8 +62,8 @@ resource "google_access_context_manager_service_perimeter" "regular" { ]) dynamic "egress_policies" { - for_each = spec.value.egress_policies == null ? [] : [ - for k in spec.value.egress_policies : + for_each = [ + for k in coalesce(spec.value.egress_policies, []) : merge(local.egress_policies[k], { key = k }) ] iterator = policy @@ -135,8 +143,8 @@ resource "google_access_context_manager_service_perimeter" "regular" { } dynamic "ingress_policies" { - for_each = spec.value.ingress_policies == null ? [] : [ - for k in spec.value.ingress_policies : + for_each = [ + for k in coalesce(spec.value.ingress_policies, []) : merge(local.ingress_policies[k], { key = k }) ] iterator = policy @@ -241,8 +249,8 @@ resource "google_access_context_manager_service_perimeter" "regular" { ]) dynamic "egress_policies" { - for_each = status.value.egress_policies == null ? [] : [ - for k in status.value.egress_policies : + for_each = [ + for k in coalesce(status.value.egress_policies, []) : merge(local.egress_policies[k], { key = k }) ] iterator = policy @@ -321,8 +329,8 @@ resource "google_access_context_manager_service_perimeter" "regular" { } dynamic "ingress_policies" { - for_each = status.value.ingress_policies == null ? [] : [ - for k in status.value.ingress_policies : + for_each = [ + for k in coalesce(status.value.ingress_policies, []) : merge(local.ingress_policies[k], { key = k }) ] iterator = policy @@ -406,6 +414,16 @@ resource "google_access_context_manager_service_perimeter" "regular" { } } + lifecycle { + precondition { + condition = length(local._undefined_ingress_policies[each.key]) == 0 + error_message = "Undefined ingress policies: ${join(", ", local._undefined_ingress_policies[each.key])}" + } + precondition { + condition = length(local._undefined_egress_policies[each.key]) == 0 + error_message = "Undefined egress policies: ${join(", ", local._undefined_egress_policies[each.key])}" + } + } depends_on = [ google_access_context_manager_access_policy.default, google_access_context_manager_access_level.basic diff --git a/modules/vpc-sc/variables.tf b/modules/vpc-sc/variables.tf index 0548a10a3..78e73d1c4 100644 --- a/modules/vpc-sc/variables.tf +++ b/modules/vpc-sc/variables.tf @@ -226,22 +226,22 @@ variable "perimeters" { title = optional(string) use_explicit_dry_run_spec = optional(bool, false) spec = optional(object({ - access_levels = optional(list(string)) - egress_policies = optional(list(string)) - ingress_policies = optional(list(string)) - restricted_services = optional(list(string)) - resources = optional(list(string)) + access_levels = optional(list(string), []) + egress_policies = optional(list(string), []) + ingress_policies = optional(list(string), []) + restricted_services = optional(list(string), []) + resources = optional(list(string), []) vpc_accessible_services = optional(object({ allowed_services = list(string) enable_restriction = optional(bool, true) })) })) status = optional(object({ - access_levels = optional(list(string)) - egress_policies = optional(list(string)) - ingress_policies = optional(list(string)) - resources = optional(list(string)) - restricted_services = optional(list(string)) + access_levels = optional(list(string), []) + egress_policies = optional(list(string), []) + ingress_policies = optional(list(string), []) + resources = optional(list(string), []) + restricted_services = optional(list(string), []) vpc_accessible_services = optional(object({ allowed_services = list(string) enable_restriction = optional(bool, true)