diff --git a/CHANGELOG.md b/CHANGELOG.md index 0331ab58..7f09ee56 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ All notable changes to this project will be documented in this file. ## [Unreleased] - **incompatible change** updated resource name for `google_dns_policy` on the `net-vpc` module +- added support for VPC-SC Ingress Egress policies on the `vpc-sc` module ## [4.8.0] - 2021-05-12 diff --git a/modules/vpc-sc/README.md b/modules/vpc-sc/README.md index 0191b651..2c64bb1c 100644 --- a/modules/vpc-sc/README.md +++ b/modules/vpc-sc/README.md @@ -28,10 +28,49 @@ module "vpc-sc" { } } access_level_perimeters = { - my_trusted_proxy = { + enforced = { my_trusted_proxy = ["perimeter"] } } + ingress_policies = { + ingress_1 = { + ingress_from = { + identity_type = "ANY_IDENTITY" + } + ingress_to = { + resources = ["*"] + operations = { + "storage.googleapis.com" = [{ method = "google.storage.objects.create" }] + "bigquery.googleapis.com" = [{ method = "BigQueryStorage.ReadRows" }] + } + } + } + } + ingress_policies_perimeters = { + enforced = { + ingress_1 = ["default"] + } + } + + egress_policies = { + egress_1 = { + egress_from = { + identity_type = "ANY_USER_ACCOUNT" + } + egress_to = { + resources = ["*"] + operations = { + "storage.googleapis.com" = [{ method = "google.storage.objects.create" }], + "bigquery.googleapis.com" = [{ method = "BigQueryStorage.ReadRows" },{ method = "TableService.ListTables" }, { permission = "bigquery.jobs.get" }] + } + } + } + } + egress_policies_perimeters = { + enforced = { + egress_1 = ["perimeter"] + } + } perimeters = { perimeter = { type = "PERIMETER_TYPE_REGULAR" @@ -106,6 +145,10 @@ module "vpc-sc" { | organization_id | Organization id in organizations/nnnnnn format. | string | ✓ | | | *access_level_perimeters* | Enforced mode -> Access Level -> Perimeters mapping. Enforced mode can be 'enforced' or 'dry_run' | map(map(list(string))) | | {} | | *access_levels* | Map of Access Levels to be created. For each Access Level you can specify 'ip_subnetworks, required_access_levels, members, negate or regions'. | map(object({...})) | | {} | +| *egress_policies* | List of EgressPolicies in the form described in the [documentation](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/access_context_manager_service_perimeter#egress_policies) | | | null | +| *egress_policies_perimeters* | Enforced mode -> Egress Policy -> Perimeters mapping. Enforced mode can be 'enforced' or 'dry_run' | map(map(list(string))) | | {} | +| *ingress_policies* | List of IngressPolicies in the form described in the [documentation](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/access_context_manager_service_perimeter#ingress_policies) | | | null | +| *ingress_policies_perimeters* | Enforced mode -> Ingress Policy -> Perimeters mapping. Enforced mode can be 'enforced' or 'dry_run' | map(map(list(string))) | | {} | | *perimeter_projects* | Perimeter -> Enforced Mode -> Projects Number mapping. Enforced mode can be 'enforced' or 'dry_run'. | map(map(list(number))) | | {} | | *perimeters* | Set of Perimeters. | map(object({...})) | | {} | diff --git a/modules/vpc-sc/main.tf b/modules/vpc-sc/main.tf index 489643c4..876dd9ba 100644 --- a/modules/vpc-sc/main.tf +++ b/modules/vpc-sc/main.tf @@ -27,8 +27,12 @@ locals { key => value if value.type == "PERIMETER_TYPE_BRIDGE" } - perimeter_access_levels_enforced = try(transpose(var.access_level_perimeters.enforced), null) - perimeter_access_levels_dry_run = try(transpose(var.access_level_perimeters.dry_run), null) + perimeter_access_levels_enforced = try(transpose(var.access_level_perimeters.enforced), null) + perimeter_access_levels_dry_run = try(transpose(var.access_level_perimeters.dry_run), null) + perimeter_ingress_policies_enforced = try(transpose(var.ingress_policies_perimeters.enforced), null) + perimeter_ingress_policies_dry_run = try(transpose(var.ingress_policies_perimeters.dry_run), null) + perimeter_egress_policies_enforced = try(transpose(var.egress_policies_perimeters.enforced), null) + perimeter_egress_policies_dry_run = try(transpose(var.egress_policies_perimeters.dry_run), null) } resource "google_access_context_manager_access_policy" "default" { @@ -89,6 +93,93 @@ resource "google_access_context_manager_service_perimeter" "standard" { allowed_services = each.value.enforced_config.vpc_accessible_services } } + + dynamic "egress_policies" { + for_each = try(local.perimeter_egress_policies_enforced[each.key] != null ? local.perimeter_egress_policies_enforced[each.key] : [], []) + + content { + dynamic "egress_from" { + for_each = try(var.egress_policies[egress_policies.value].egress_from != null ? [""] : [], []) + + content { + identity_type = try(var.egress_policies[egress_policies.value].egress_from.identity_type, null) + identities = try(var.egress_policies[egress_policies.value].egress_from.identities, null) + } + } + dynamic "egress_to" { + for_each = try(var.egress_policies[egress_policies.value].egress_to != null ? [""] : [], []) + + content { + resources = try(var.egress_policies[egress_policies.value].egress_to.resources, null) + + dynamic "operations" { + for_each = try(var.egress_policies[egress_policies.value].egress_to.operations, []) + + content { + service_name = try(operations.key, null) + + dynamic "method_selectors" { + for_each = try(operations.value, []) + + content { + method = try(method_selectors.value.method, null) + permission = try(method_selectors.value.permission, null) + } + } + } + } + } + } + } + } + + dynamic "ingress_policies" { + for_each = try(local.perimeter_ingress_policies_enforced[each.key] != null ? local.perimeter_ingress_policies_enforced[each.key] : [], []) + + content { + dynamic "ingress_from" { + for_each = try(var.ingress_policies[ingress_policies.value].ingress_from != null ? [""] : [], []) + + content { + identity_type = try(var.ingress_policies[ingress_policies.value].ingress_from.identity_type, null) + identities = try(var.ingress_policies[ingress_policies.value].ingress_from.identities, null) + + dynamic "sources" { + for_each = toset(try([var.ingress_policies[ingress_policies.value].ingress_from.sources], [])) + + content { + access_level = try(sources.value.access_level, null) + resource = try(sources.value.resource, null) + } + } + } + } + dynamic "ingress_to" { + for_each = try(var.ingress_policies[ingress_policies.value].ingress_to != null ? [""] : [], []) + + content { + resources = try(var.ingress_policies[ingress_policies.value].ingress_to.resources, null) + + dynamic "operations" { + for_each = try(var.ingress_policies[ingress_policies.value].ingress_to.operations, []) + + content { + service_name = try(operations.key, null) + + dynamic "method_selectors" { + for_each = try(operations.value, []) + + content { + method = try(method_selectors.value.method, null) + permission = try(method_selectors.value.permission, null) + } + } + } + } + } + } + } + } } } @@ -115,6 +206,93 @@ resource "google_access_context_manager_service_perimeter" "standard" { allowed_services = try(each.value.dry_run_config.vpc_accessible_services, null) } } + + dynamic "egress_policies" { + for_each = try(local.perimeter_egress_policies_dry_run[each.key] != null ? local.perimeter_egress_policies_dry_run[each.key] : [], []) + + content { + dynamic "egress_from" { + for_each = try(var.egress_policies[egress_policies.value].egress_from != null ? [""] : [], []) + + content { + identity_type = try(var.egress_policies[egress_policies.value].egress_from.identity_type, null) + identities = try(var.egress_policies[egress_policies.value].egress_from.identities, null) + } + } + dynamic "egress_to" { + for_each = try(var.egress_policies[egress_policies.value].egress_to != null ? [""] : [], []) + + content { + resources = try(var.egress_policies[egress_policies.value].egress_to.resources, null) + + dynamic "operations" { + for_each = try(var.egress_policies[egress_policies.value].egress_to.operations, []) + + content { + service_name = try(operations.key, null) + + dynamic "method_selectors" { + for_each = try(operations.value, []) + + content { + method = try(method_selectors.value.method, null) + permission = try(method_selectors.value.permission, null) + } + } + } + } + } + } + } + } + + dynamic "ingress_policies" { + for_each = try(local.perimeter_ingress_policies_dry_run[each.key] != null ? local.perimeter_ingress_policies_dry_run[each.key] : [], []) + + content { + dynamic "ingress_from" { + for_each = try(var.ingress_policies[ingress_policies.value].ingress_from != null ? [""] : [], []) + + content { + identity_type = try(var.ingress_policies[ingress_policies.value].ingress_from.identity_type, null) + identities = try(var.ingress_policies[ingress_policies.value].ingress_from.identities, null) + + dynamic "sources" { + for_each = toset(try([var.ingress_policies[ingress_policies.value].ingress_from.sources], [])) + + content { + access_level = try(sources.value.access_level, null) + resource = try(sources.value.resource, null) + } + } + } + } + dynamic "ingress_to" { + for_each = try(var.ingress_policies[ingress_policies.value].ingress_to != null ? [""] : [], []) + + content { + resources = try(var.ingress_policies[ingress_policies.value].ingress_to.resources, null) + + dynamic "operations" { + for_each = try(var.ingress_policies[ingress_policies.value].ingress_to.operations, []) + + content { + service_name = try(operations.key, null) + + dynamic "method_selectors" { + for_each = try(operations.value, []) + + content { + method = try(method_selectors.value.method, null) + permission = try(method_selectors.value.permission, null) + } + } + } + } + } + } + } + } } } diff --git a/modules/vpc-sc/variables.tf b/modules/vpc-sc/variables.tf index f90c3026..490f4951 100644 --- a/modules/vpc-sc/variables.tf +++ b/modules/vpc-sc/variables.tf @@ -40,6 +40,28 @@ variable "access_policy_title" { type = string } +variable "egress_policies" { + description = "List of EgressPolicies in the form described in the [documentation](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/access_context_manager_service_perimeter#egress_policies)" + default = null +} + +variable "egress_policies_perimeters" { + description = "Enforced mode -> Egress Policy -> Perimeters mapping. Enforced mode can be 'enforced' or 'dry_run'" + type = map(map(list(string))) + default = {} +} + +variable "ingress_policies" { + description = "List of IngressPolicies in the form described in the [documentation](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/access_context_manager_service_perimeter#ingress_policies)" + default = null +} + +variable "ingress_policies_perimeters" { + description = "Enforced mode -> Ingress Policy -> Perimeters mapping. Enforced mode can be 'enforced' or 'dry_run'" + type = map(map(list(string))) + default = {} +} + variable "organization_id" { description = "Organization id in organizations/nnnnnn format." type = string