diff --git a/examples/automatic_folder/watcher.tf b/examples/automatic_folder/watcher.tf index 7a6dbf8a..93c9f266 100644 --- a/examples/automatic_folder/watcher.tf +++ b/examples/automatic_folder/watcher.tf @@ -16,7 +16,7 @@ module "event_folder_log_entry" { source = "terraform-google-modules/event-function/google//modules/event-folder-log-entry" - version = "~> 4.0" + version = "~> 5.0" filter = < "resource" }, - { for k, v in lookup(lookup(ingress_policies.value["from"], "sources", {}), "access_levels", []) : v => "access_level" }) - content { - resource = sources.value == "resource" ? sources.key : null - access_level = sources.value == "access_level" ? sources.key != "*" ? "accessPolicies/${var.policy}/accessLevels/${sources.key}" : "*" : null - } - } - identity_type = lookup(ingress_policies.value["from"], "identity_type", null) - identities = lookup(ingress_policies.value["from"], "identities", null) - } - ingress_to { - resources = lookup(ingress_policies.value["to"], "resources", ["*"]) - dynamic "operations" { - for_each = lookup(ingress_policies.value["to"], "operations", []) - content { - service_name = operations.key - dynamic "method_selectors" { - for_each = operations.key != "*" ? merge( - { for v in lookup(operations.value, "methods", []) : v => "method" }, - { for v in lookup(operations.value, "permissions", []) : v => "permission" }) : {} - content { - method = method_selectors.value == "method" ? method_selectors.key : null - permission = method_selectors.value == "permission" ? method_selectors.key : null - } - } - } - } - } - } - } - dynamic "egress_policies" { - for_each = var.egress_policies - iterator = egress_policies - content { - egress_from { - identity_type = lookup(egress_policies.value["from"], "identity_type", null) - identities = lookup(egress_policies.value["from"], "identities", null) - dynamic "sources" { - for_each = { for k, v in lookup(lookup(egress_policies.value["from"], "sources", {}), "access_levels", []) : v => "access_level" } - content { - access_level = sources.value == "access_level" ? sources.key != "*" ? "accessPolicies/${var.policy}/accessLevels/${sources.key}" : "*" : null - } - } - source_restriction = lookup(egress_policies.value["from"], "sources", null) != null ? "SOURCE_RESTRICTION_ENABLED" : null - } - egress_to { - resources = lookup(egress_policies.value["to"], "resources", ["*"]) - external_resources = lookup(egress_policies.value["to"], "external_resources", []) - dynamic "operations" { - for_each = lookup(egress_policies.value["to"], "operations", []) - content { - service_name = operations.key - dynamic "method_selectors" { - for_each = operations.key != "*" ? merge( - { for v in lookup(operations.value, "methods", []) : v => "method" }, - { for v in lookup(operations.value, "permissions", []) : v => "permission" }) : {} - content { - method = method_selectors.value == "method" ? method_selectors.key : null - permission = method_selectors.value == "permission" ? method_selectors.key : null - } - } - } - } - } - } - } dynamic "vpc_accessible_services" { for_each = contains(var.vpc_accessible_services, "*") ? [] : [var.vpc_accessible_services] @@ -128,79 +55,7 @@ resource "google_access_context_manager_service_perimeter" "regular_service_peri var.access_levels_dry_run ) - dynamic "ingress_policies" { - for_each = var.ingress_policies_dry_run - iterator = ingress_policies_dry_run - content { - ingress_from { - dynamic "sources" { - for_each = merge( - { for k, v in lookup(lookup(ingress_policies_dry_run.value["from"], "sources", {}), "resources", []) : v => "resource" }, - { for k, v in lookup(lookup(ingress_policies_dry_run.value["from"], "sources", {}), "access_levels", []) : v => "access_level" }) - content { - resource = sources.value == "resource" ? sources.key : null - access_level = sources.value == "access_level" ? sources.key != "*" ? "accessPolicies/${var.policy}/accessLevels/${sources.key}" : "*" : null - } - } - identity_type = lookup(ingress_policies_dry_run.value["from"], "identity_type", null) - identities = lookup(ingress_policies_dry_run.value["from"], "identities", null) - } - ingress_to { - resources = lookup(ingress_policies_dry_run.value["to"], "resources", ["*"]) - dynamic "operations" { - for_each = lookup(ingress_policies_dry_run.value["to"], "operations", []) - content { - service_name = operations.key - dynamic "method_selectors" { - for_each = operations.key != "*" ? merge( - { for v in lookup(operations.value, "methods", []) : v => "method" }, - { for v in lookup(operations.value, "permissions", []) : v => "permission" }) : {} - content { - method = method_selectors.value == "method" ? method_selectors.key : null - permission = method_selectors.value == "permission" ? method_selectors.key : null - } - } - } - } - } - } - } - dynamic "egress_policies" { - for_each = var.egress_policies_dry_run - iterator = egress_policies_dry_run - content { - egress_from { - identity_type = lookup(egress_policies_dry_run.value["from"], "identity_type", null) - identities = lookup(egress_policies_dry_run.value["from"], "identities", null) - dynamic "sources" { - for_each = { for k, v in lookup(lookup(egress_policies_dry_run.value["from"], "sources", {}), "access_levels", []) : v => "access_level" } - content { - access_level = sources.value == "access_level" ? sources.key != "*" ? "accessPolicies/${var.policy}/accessLevels/${sources.key}" : "*" : null - } - } - source_restriction = lookup(egress_policies_dry_run.value["from"], "sources", null) != null ? "SOURCE_RESTRICTION_ENABLED" : null - } - egress_to { - resources = lookup(egress_policies_dry_run.value["to"], "resources", ["*"]) - dynamic "operations" { - for_each = lookup(egress_policies_dry_run.value["to"], "operations", []) - content { - service_name = operations.key - dynamic "method_selectors" { - for_each = operations.key != "*" ? merge( - { for v in lookup(operations.value, "methods", []) : v => "method" }, - { for v in lookup(operations.value, "permissions", []) : v => "permission" }) : {} - content { - method = method_selectors.value == "method" ? method_selectors.key : null - permission = method_selectors.value == "permission" ? method_selectors.key : null - } - } - } - } - } - } - } dynamic "vpc_accessible_services" { for_each = contains(var.vpc_accessible_services_dry_run, "*") ? [] : [var.vpc_accessible_services_dry_run] @@ -214,7 +69,11 @@ resource "google_access_context_manager_service_perimeter" "regular_service_peri use_explicit_dry_run_spec = local.dry_run lifecycle { - ignore_changes = [status[0].resources] + ignore_changes = [ + status[0].resources, + status[0].ingress_policies, # Allows ingress policies to be managed by google_access_context_manager_service_perimeter_ingress_policy resources + status[0].egress_policies # Allows egress policies to be managed by google_access_context_manager_service_perimeter_egress_policy resources + ] } } diff --git a/modules/regular_service_perimeter/versions.tf b/modules/regular_service_perimeter/versions.tf index 1afce9a6..11e46767 100644 --- a/modules/regular_service_perimeter/versions.tf +++ b/modules/regular_service_perimeter/versions.tf @@ -20,7 +20,7 @@ terraform { google = { source = "hashicorp/google" - version = ">= 5.4, < 7" + version = ">= 6.21, < 7" } } diff --git a/modules/regular_service_perimeter/vpc-sc-policies.tf b/modules/regular_service_perimeter/vpc-sc-policies.tf new file mode 100644 index 00000000..93aec13b --- /dev/null +++ b/modules/regular_service_perimeter/vpc-sc-policies.tf @@ -0,0 +1,184 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +resource "google_access_context_manager_service_perimeter_ingress_policy" "ingress_policies" { + for_each = { for k, v in var.ingress_policies : k => v } + + perimeter = google_access_context_manager_service_perimeter.regular_service_perimeter.name + title = "Ingress Policy ${each.key}" + ingress_from { + dynamic "sources" { + for_each = merge( + { for k, v in lookup(lookup(each.value["from"], "sources", {}), "resources", []) : v => "resource" }, + { for k, v in lookup(lookup(each.value["from"], "sources", {}), "access_levels", []) : v => "access_level" }) + content { + resource = sources.value == "resource" ? sources.key : null + access_level = sources.value == "access_level" ? sources.key != "*" ? "accessPolicies/${var.policy}/accessLevels/${sources.key}" : "*" : null + } + } + identity_type = lookup(each.value["from"], "identity_type", null) + identities = lookup(each.value["from"], "identities", null) + } + + ingress_to { + resources = lookup(each.value["to"], "resources", ["*"]) + dynamic "operations" { + for_each = lookup(each.value["to"], "operations", []) + content { + service_name = operations.key + dynamic "method_selectors" { + for_each = operations.key != "*" ? merge( + { for v in lookup(operations.value, "methods", []) : v => "method" }, + { for v in lookup(operations.value, "permissions", []) : v => "permission" }) : {} + content { + method = method_selectors.value == "method" ? method_selectors.key : null + permission = method_selectors.value == "permission" ? method_selectors.key : null + } + } + } + } + } + lifecycle { + create_before_destroy = true + } +} + +resource "google_access_context_manager_service_perimeter_egress_policy" "egress_policies" { + for_each = { for k, v in var.egress_policies : k => v } + + perimeter = google_access_context_manager_service_perimeter.regular_service_perimeter.name + title = "Egress Policy ${each.key}" + + egress_from { + identity_type = lookup(each.value["from"], "identity_type", null) + identities = lookup(each.value["from"], "identities", null) + dynamic "sources" { + for_each = { for k, v in lookup(lookup(each.value["from"], "sources", {}), "access_levels", []) : v => "access_level" } + content { + access_level = sources.value == "access_level" ? sources.key != "*" ? "accessPolicies/${var.policy}/accessLevels/${sources.key}" : "*" : null + } + } + source_restriction = lookup(each.value["from"], "sources", null) != null ? "SOURCE_RESTRICTION_ENABLED" : null + } + egress_to { + resources = lookup(each.value["to"], "resources", ["*"]) + external_resources = lookup(each.value["to"], "external_resources", []) + dynamic "operations" { + for_each = lookup(each.value["to"], "operations", []) + content { + service_name = operations.key + dynamic "method_selectors" { + for_each = operations.key != "*" ? merge( + { for v in lookup(operations.value, "methods", []) : v => "method" }, + { for v in lookup(operations.value, "permissions", []) : v => "permission" }) : {} + content { + method = method_selectors.value == "method" ? method_selectors.key : null + permission = method_selectors.value == "permission" ? method_selectors.key : null + } + } + } + } + } + lifecycle { + create_before_destroy = true + } +} + +############################################ +# DRY-RUN POLICIES # +############################################ + +resource "google_access_context_manager_service_perimeter_dry_run_ingress_policy" "ingress_policies" { + for_each = { for k, v in var.ingress_policies_dry_run : k => v } + + perimeter = google_access_context_manager_service_perimeter.regular_service_perimeter.name + title = "Ingress Policy ${each.key}" + ingress_from { + dynamic "sources" { + for_each = merge( + { for k, v in lookup(lookup(each.value["from"], "sources", {}), "resources", []) : v => "resource" }, + { for k, v in lookup(lookup(each.value["from"], "sources", {}), "access_levels", []) : v => "access_level" }) + content { + resource = sources.value == "resource" ? sources.key : null + access_level = sources.value == "access_level" ? sources.key != "*" ? "accessPolicies/${var.policy}/accessLevels/${sources.key}" : "*" : null + } + } + identity_type = lookup(each.value["from"], "identity_type", null) + identities = lookup(each.value["from"], "identities", null) + } + + ingress_to { + resources = lookup(each.value["to"], "resources", ["*"]) + dynamic "operations" { + for_each = lookup(each.value["to"], "operations", []) + content { + service_name = operations.key + dynamic "method_selectors" { + for_each = operations.key != "*" ? merge( + { for v in lookup(operations.value, "methods", []) : v => "method" }, + { for v in lookup(operations.value, "permissions", []) : v => "permission" }) : {} + content { + method = method_selectors.value == "method" ? method_selectors.key : null + permission = method_selectors.value == "permission" ? method_selectors.key : null + } + } + } + } + } + lifecycle { + create_before_destroy = true + } +} + +resource "google_access_context_manager_service_perimeter_dry_run_egress_policy" "egress_policies" { + for_each = { for k, v in var.egress_policies_dry_run : k => v } + + perimeter = google_access_context_manager_service_perimeter.regular_service_perimeter.name + title = "Egress Policy ${each.key}" + + egress_from { + identity_type = lookup(each.value["from"], "identity_type", null) + identities = lookup(each.value["from"], "identities", null) + dynamic "sources" { + for_each = { for k, v in lookup(lookup(each.value["from"], "sources", {}), "access_levels", []) : v => "access_level" } + content { + access_level = sources.value == "access_level" ? sources.key != "*" ? "accessPolicies/${var.policy}/accessLevels/${sources.key}" : "*" : null + } + } + source_restriction = lookup(each.value["from"], "sources", null) != null ? "SOURCE_RESTRICTION_ENABLED" : null + } + egress_to { + resources = lookup(each.value["to"], "resources", ["*"]) + dynamic "operations" { + for_each = lookup(each.value["to"], "operations", []) + content { + service_name = operations.key + dynamic "method_selectors" { + for_each = operations.key != "*" ? merge( + { for v in lookup(operations.value, "methods", []) : v => "method" }, + { for v in lookup(operations.value, "permissions", []) : v => "permission" }) : {} + content { + method = method_selectors.value == "method" ? method_selectors.key : null + permission = method_selectors.value == "permission" ? method_selectors.key : null + } + } + } + } + } + lifecycle { + create_before_destroy = true + } +}