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)