diff --git a/0-bootstrap/README-GitHub.md b/0-bootstrap/README-GitHub.md index 8b03f0040..37918ec61 100644 --- a/0-bootstrap/README-GitHub.md +++ b/0-bootstrap/README-GitHub.md @@ -565,12 +565,11 @@ or go to [Deploying step 3-networks-hub-and-spoke](#deploying-step-3-networks-hu chmod 755 ./tf-wrapper.sh ``` -1. Rename `common.auto.example.tfvars` to `common.auto.tfvars`, rename `production.auto.example.tfvars` to `production.auto.tfvars` and rename `access_context.auto.example.tfvars` to `access_context.auto.tfvars`. +1. Rename `common.auto.example.tfvars` to `common.auto.tfvars`, and rename `production.auto.example.tfvars` to `production.auto.tfvars`. ```bash mv common.auto.example.tfvars common.auto.tfvars mv production.auto.example.tfvars production.auto.tfvars - mv access_context.auto.example.tfvars access_context.auto.tfvars ``` 1. Update the file `production.auto.tfvars` with the values for the `target_name_server_addresses`. @@ -665,7 +664,7 @@ An environment variable `GOOGLE_IMPERSONATE_SERVICE_ACCOUNT` will be set with th 1. Push your production branch since development and nonproduction depends it. -*Note:** The Production envrionment must be the first branch to be pushed as it includes the DNS Hub communication that will be used by other environments. +*Note:** The Production environment must be the first branch to be pushed as it includes the DNS Hub communication that will be used by other environments. ```bash git add . @@ -762,12 +761,11 @@ An environment variable `GOOGLE_IMPERSONATE_SERVICE_ACCOUNT` will be set with th chmod 755 ./tf-wrapper.sh ``` -1. Rename `common.auto.example.tfvars` to `common.auto.tfvars`, rename `shared.auto.example.tfvars` to `shared.auto.tfvars` and rename `access_context.auto.example.tfvars` to `access_context.auto.tfvars`. +1. Rename `common.auto.example.tfvars` to `common.auto.tfvars`, and rename `shared.auto.example.tfvars` to `shared.auto.tfvars`. ```bash mv common.auto.example.tfvars common.auto.tfvars mv shared.auto.example.tfvars shared.auto.tfvars - mv access_context.auto.example.tfvars access_context.auto.tfvars ``` 1. Update `common.auto.tfvars` file with values from your GCP environment. diff --git a/0-bootstrap/README-GitLab.md b/0-bootstrap/README-GitLab.md index 56adbb6c0..8035648b9 100644 --- a/0-bootstrap/README-GitLab.md +++ b/0-bootstrap/README-GitLab.md @@ -568,12 +568,11 @@ or go to [Deploying step 3-networks-hub-and-spoke](#deploying-step-3-networks-hu chmod 755 ./*.sh ``` -1. Rename `common.auto.example.tfvars` to `common.auto.tfvars`, rename `production.auto.example.tfvars` to `production.auto.tfvars` and rename `access_context.auto.example.tfvars` to `access_context.auto.tfvars`. +1. Rename `common.auto.example.tfvars` to `common.auto.tfvars`, and rename `production.auto.example.tfvars` to `production.auto.tfvars`. ```bash mv common.auto.example.tfvars common.auto.tfvars mv production.auto.example.tfvars production.auto.tfvars - mv access_context.auto.example.tfvars access_context.auto.tfvars ``` 1. Update the file `production.auto.tfvars` with the values for the `target_name_server_addresses`. @@ -668,7 +667,7 @@ An environment variable `GOOGLE_IMPERSONATE_SERVICE_ACCOUNT` will be set with th 1. Push your production branch since development and nonproduction depends it. -*Note:** The Production envrionment must be the first branch to be pushed as it includes the DNS Hub communication that will be used by other environments. +*Note:** The Production environment must be the first branch to be pushed as it includes the DNS Hub communication that will be used by other environments. ```bash git add . @@ -742,12 +741,11 @@ An environment variable `GOOGLE_IMPERSONATE_SERVICE_ACCOUNT` will be set with th chmod 755 ./*.sh ``` -1. Rename `common.auto.example.tfvars` to `common.auto.tfvars`, rename `shared.auto.example.tfvars` to `shared.auto.tfvars` and rename `access_context.auto.example.tfvars` to `access_context.auto.tfvars`. +1. Rename `common.auto.example.tfvars` to `common.auto.tfvars`, and rename `shared.auto.example.tfvars` to `shared.auto.tfvars`. ```bash mv common.auto.example.tfvars common.auto.tfvars mv shared.auto.example.tfvars shared.auto.tfvars - mv access_context.auto.example.tfvars access_context.auto.tfvars ``` 1. Update `common.auto.tfvars` file with values from your GCP environment. diff --git a/0-bootstrap/README-Jenkins.md b/0-bootstrap/README-Jenkins.md index 7d880bb68..8d4cc9cb1 100644 --- a/0-bootstrap/README-Jenkins.md +++ b/0-bootstrap/README-Jenkins.md @@ -599,12 +599,11 @@ Here you will configure a VPN Network tunnel to enable connectivity between the sed -i'' -e "s/CICD_PROJECT_ID/${CICD_PROJECT_ID}/" ./Jenkinsfile ``` -1. Rename `common.auto.example.tfvars` to `common.auto.tfvars`, rename `production.auto.example.tfvars` to `production.auto.tfvars` and rename `access_context.auto.example.tfvars` to `access_context.auto.tfvars`. +1. Rename `common.auto.example.tfvars` to `common.auto.tfvars`, and rename `production.auto.example.tfvars` to `production.auto.tfvars`. ```bash mv common.auto.example.tfvars common.auto.tfvars mv production.auto.example.tfvars production.auto.tfvars - mv access_context.auto.example.tfvars access_context.auto.tfvars ``` 1. Update `common.auto.tfvars` file with values from your environment and bootstrap. See any of the envs folder [README.md](../3-networks-svpc/envs/production/README.md) files for additional information on the values in the `common.auto.tfvars` file. @@ -752,12 +751,11 @@ Here you will configure a VPN Network tunnel to enable connectivity between the sed -i'' -e "s/CICD_PROJECT_ID/${CICD_PROJECT_ID}/" ./Jenkinsfile ``` -1. Rename `common.auto.example.tfvars` to `common.auto.tfvars`, rename `shared.auto.example.tfvars` to `shared.auto.tfvars` and rename `access_context.auto.example.tfvars` to `access_context.auto.tfvars`. +1. Rename `common.auto.example.tfvars` to `common.auto.tfvars`, and rename `shared.auto.example.tfvars` to `shared.auto.tfvars`. ```bash mv common.auto.example.tfvars common.auto.tfvars mv shared.auto.example.tfvars shared.auto.tfvars - mv access_context.auto.example.tfvars access_context.auto.tfvars ``` 1. Update `common.auto.tfvars` file with values from your environment and bootstrap. See any of the envs folder [README.md](../3-networks-hub-and-spoke/envs/production/README.md) files for additional information on the values in the `common.auto.tfvars` file. diff --git a/0-bootstrap/README-Terraform-Cloud.md b/0-bootstrap/README-Terraform-Cloud.md index 81bc4bf05..8ef4cc932 100644 --- a/0-bootstrap/README-Terraform-Cloud.md +++ b/0-bootstrap/README-Terraform-Cloud.md @@ -476,12 +476,11 @@ or go to [Deploying step 3-networks-hub-and-spoke](#deploying-step-3-networks-hu chmod 755 ./tf-wrapper.sh ``` -1. Rename `common.auto.example.tfvars` to `common.auto.tfvars`, rename `production.auto.example.tfvars` to `production.auto.tfvars` and rename `access_context.auto.example.tfvars` to `access_context.auto.tfvars`. +1. Rename `common.auto.example.tfvars` to `common.auto.tfvars`, and rename `production.auto.example.tfvars` to `production.auto.tfvars`. ```bash mv common.auto.example.tfvars common.auto.tfvars mv production.auto.example.tfvars production.auto.tfvars - mv access_context.auto.example.tfvars access_context.auto.tfvars ``` 1. Update the file `production.auto.tfvars` with the values for the `target_name_server_addresses`. @@ -639,12 +638,11 @@ An environment variable `GOOGLE_IMPERSONATE_SERVICE_ACCOUNT` will be set with th chmod 755 ./tf-wrapper.sh ``` -1. Rename `common.auto.example.tfvars` to `common.auto.tfvars`, rename `shared.auto.example.tfvars` to `shared.auto.tfvars` and rename `access_context.auto.example.tfvars` to `access_context.auto.tfvars`. +1. Rename `common.auto.example.tfvars` to `common.auto.tfvars`, and rename `shared.auto.example.tfvars` to `shared.auto.tfvars`. ```bash mv common.auto.example.tfvars common.auto.tfvars mv shared.auto.example.tfvars shared.auto.tfvars - mv access_context.auto.example.tfvars access_context.auto.tfvars ``` 1. Update `common.auto.tfvars` file with values from your GCP environment. diff --git a/0-bootstrap/README.md b/0-bootstrap/README.md index 15a339411..87baa2a0b 100644 --- a/0-bootstrap/README.md +++ b/0-bootstrap/README.md @@ -546,6 +546,7 @@ The following steps will guide you through deploying without using Cloud Build. | cloud\_build\_worker\_range\_id | The Cloud Build private worker IP range ID. | | cloud\_builder\_artifact\_repo | Artifact Registry (AR) Repository created to store TF Cloud Builder images. | | cloudbuild\_project\_id | Project where Cloud Build configuration and terraform container image will reside. | +| cloudbuild\_project\_number | The cloudbuild project number. | | common\_config | Common configuration data to be used in other steps. | | csr\_repos | List of Cloud Source Repos created by the module, linked to Cloud Build triggers. | | environment\_step\_terraform\_service\_account\_email | Environment Step Terraform Account | @@ -555,9 +556,11 @@ The following steps will guide you through deploying without using Cloud Build. | networks\_step\_terraform\_service\_account\_email | Networks Step Terraform Account | | optional\_groups | List of Google Groups created that are optional to the Example Foundation steps. | | organization\_step\_terraform\_service\_account\_email | Organization Step Terraform Account | +| parent\_id | Parent ID service account. | | projects\_gcs\_bucket\_tfstate | Bucket used for storing terraform state for stage 4-projects foundations pipelines in seed project. | | projects\_step\_terraform\_service\_account\_email | Projects Step Terraform Account | | required\_groups | List of Google Groups created that are required by the Example Foundation steps. | | seed\_project\_id | Project where service accounts and core APIs will be enabled. | +| seed\_project\_number | The seed project number. | diff --git a/0-bootstrap/cb.tf b/0-bootstrap/cb.tf index eae95b617..fa77b4ea0 100644 --- a/0-bootstrap/cb.tf +++ b/0-bootstrap/cb.tf @@ -280,3 +280,8 @@ resource "google_sourcerepo_repository_iam_member" "member" { depends_on = [module.tf_source] } + +data "google_project" "cloudbuild_project" { + project_id = module.tf_source.cloudbuild_project_id + depends_on = [module.tf_source] +} diff --git a/0-bootstrap/main.tf b/0-bootstrap/main.tf index 44ca2faf0..81d7e634f 100644 --- a/0-bootstrap/main.tf +++ b/0-bootstrap/main.tf @@ -106,3 +106,8 @@ module "seed_bootstrap" { depends_on = [module.required_group] } + +data "google_project" "seed_project" { + project_id = module.seed_bootstrap.seed_project_id + depends_on = [module.seed_bootstrap] +} diff --git a/0-bootstrap/modules/cb-private-pool/README.md b/0-bootstrap/modules/cb-private-pool/README.md index 1e6969997..cac726f5c 100644 --- a/0-bootstrap/modules/cb-private-pool/README.md +++ b/0-bootstrap/modules/cb-private-pool/README.md @@ -3,8 +3,9 @@ | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| private\_worker\_pool | name: Name of the worker pool. A name with a random suffix is generated if not set.
region: The private worker pool region. See https://cloud.google.com/build/docs/locations for available locations.
disk\_size\_gb: Size of the disk attached to the worker, in GB.
machine\_type: Machine type of a worker.
no\_external\_ip: If true, workers are created without any public address, which prevents network egress to public IPs.
enable\_network\_peering: Set to true to enable configuration of networking peering for the private worker pool.
create\_peered\_network: If true a network will be created to stablish the network peering.
peered\_network\_id: The ID of the existing network to configure peering for the private worker pool if create\_peered\_network false. The project containing the network must have Service Networking API (`servicenetworking.googleapis.com`) enabled.
peered\_network\_subnet\_ip: The IP range to be used for the subnet that a will created in the peered network if create\_peered\_network true.
peering\_address: The IP address or beginning of the peering address range. This can be supplied as an input to reserve a specific address or omitted to allow GCP to choose a valid one.
peering\_prefix\_length: The prefix length of the IP peering range. If not present, it means the address field is a single IP address. |
object({
name = optional(string, "")
region = optional(string, "us-central1")
disk_size_gb = optional(number, 100)
machine_type = optional(string, "e2-medium")
no_external_ip = optional(bool, false)
enable_network_peering = optional(bool, false)
create_peered_network = optional(bool, false)
peered_network_id = optional(string, "")
peered_network_subnet_ip = optional(string, "")
peering_address = optional(string, null)
peering_prefix_length = optional(number, 24)
})
| `{}` | no | +| private\_worker\_pool | name: Name of the worker pool. A name with a random suffix is generated if not set.
region: The private worker pool region. See https://cloud.google.com/build/docs/locations for available locations.
disk\_size\_gb: Size of the disk attached to the worker, in GB.
machine\_type: Machine type of a worker.
no\_external\_ip: If true, workers are created without any public address, which prevents network egress to public IPs.
enable\_network\_peering: Set to true to enable configuration of networking peering for the private worker pool.
create\_peered\_network: If true a network will be created to stablish the network peering.
peered\_network\_id: The ID of the existing network to configure peering for the private worker pool if create\_peered\_network false. The project containing the network must have Service Networking API (`servicenetworking.googleapis.com`) enabled.
peered\_network\_subnet\_ip: The IP range to be used for the subnet that a will created in the peered network if create\_peered\_network true.
peering\_address: The IP address or beginning of the peering address range. This can be supplied as an input to reserve a specific address or omitted to allow GCP to choose a valid one.
peering\_prefix\_length: The prefix length of the IP peering range. If not present, it means the address field is a single IP address. |
object({
name = optional(string, "")
region = optional(string, "us-central1")
disk_size_gb = optional(number, 100)
machine_type = optional(string, "e2-medium")
no_external_ip = optional(bool, true)
enable_network_peering = optional(bool, true)
create_peered_network = optional(bool, true)
peered_network_id = optional(string, "")
peered_network_subnet_ip = optional(string, "")
peering_address = optional(string, null)
peering_prefix_length = optional(number, 24)
})
| `{}` | no | | project\_id | ID of the project where the private pool will be created | `string` | n/a | yes | +| subnet\_region | Region where a subnet will be created in the sigle project network. | `string` | `"us-central1"` | no | | vpc\_flow\_logs | aggregation\_interval: Toggles the aggregation interval for collecting flow logs. Increasing the interval time will reduce the amount of generated flow logs for long lasting connections. Possible values are: INTERVAL\_5\_SEC, INTERVAL\_30\_SEC, INTERVAL\_1\_MIN, INTERVAL\_5\_MIN, INTERVAL\_10\_MIN, INTERVAL\_15\_MIN.
flow\_sampling: Set the sampling rate of VPC flow logs within the subnetwork where 1.0 means all collected logs are reported and 0.0 means no logs are reported. The value of the field must be in [0, 1].
metadata: Configures whether metadata fields should be added to the reported VPC flow logs. Possible values are: EXCLUDE\_ALL\_METADATA, INCLUDE\_ALL\_METADATA, CUSTOM\_METADATA.
metadata\_fields: ist of metadata fields that should be added to reported logs. Can only be specified if VPC flow logs for this subnetwork is enabled and "metadata" is set to CUSTOM\_METADATA.
filter\_expr: Export filter used to define which VPC flow logs should be logged, as as CEL expression. See https://cloud.google.com/vpc/docs/flow-logs#filtering for details on how to format this field. |
object({
aggregation_interval = optional(string, "INTERVAL_5_SEC")
flow_sampling = optional(string, "0.5")
metadata = optional(string, "INCLUDE_ALL_METADATA")
metadata_fields = optional(list(string), [])
filter_expr = optional(string, "true")
})
| `{}` | no | | vpn\_configuration | enable\_vpn: set to true to create VPN connection to on prem. If true, the following values must be valid.
on\_prem\_public\_ip\_address0: The first public IP address for on prem VPN connection.
on\_prem\_public\_ip\_address1: The second public IP address for on prem VPN connection.
router\_asn: Border Gateway Protocol (BGP) Autonomous System Number (ASN) for cloud routes.
bgp\_peer\_asn: Border Gateway Protocol (BGP) Autonomous System Number (ASN) for peer cloud routes.
shared\_secret: The shared secret used in the VPN.
psk\_secret\_project\_id: The ID of the project that contains the secret from secret manager that holds the VPN pre-shared key.
psk\_secret\_name: The name of the secret to retrieve from secret manager that holds the VPN pre-shared key.
tunnel0\_bgp\_peer\_address: BGP peer address for tunnel 0.
tunnel0\_bgp\_session\_range: BGP session range for tunnel 0.
tunnel1\_bgp\_peer\_address: BGP peer address for tunnel 1.
tunnel1\_bgp\_session\_range: BGP session range for tunnel 1. |
object({
enable_vpn = optional(bool, false)
on_prem_public_ip_address0 = optional(string, "")
on_prem_public_ip_address1 = optional(string, "")
router_asn = optional(number, 64515)
bgp_peer_asn = optional(number, 64513)
psk_secret_project_id = optional(string, "")
psk_secret_name = optional(string, "")
tunnel0_bgp_peer_address = optional(string, "")
tunnel0_bgp_session_range = optional(string, "")
tunnel1_bgp_peer_address = optional(string, "")
tunnel1_bgp_session_range = optional(string, "")
})
| `{}` | no | diff --git a/0-bootstrap/modules/cb-private-pool/network.tf b/0-bootstrap/modules/cb-private-pool/network.tf index ea5546675..f513ab74e 100644 --- a/0-bootstrap/modules/cb-private-pool/network.tf +++ b/0-bootstrap/modules/cb-private-pool/network.tf @@ -16,6 +16,26 @@ locals { peered_ip_range = var.private_worker_pool.enable_network_peering ? "${google_compute_global_address.worker_pool_range[0].address}/${google_compute_global_address.worker_pool_range[0].prefix_length}" : "" + + nat_proxy_vm_ip_range = "10.1.1.0/24" + + single_project_network = { + subnet_name = "eab-develop-us-central1" + subnet_ip = "10.1.20.0/24" + subnet_region = var.subnet_region + subnet_private_access = true + } + single_project_secondary = { + "eab-develop-us-central1" = [ + { + range_name = "eab-develop-us-central1-secondary-01" + ip_cidr_range = "192.168.0.0/18" + }, + { + range_name = "eab-develop-us-central1-secondary-02" + ip_cidr_range = "192.168.64.0/18" + }, + ] } } module "peered_network" { @@ -96,27 +116,208 @@ module "firewall_rules" { project_id = var.project_id network_name = local.peered_network_id - rules = [{ - name = "fw-b-cbpools-100-i-a-all-all-all-service-networking" - description = "allow ingres from the IPs configured for service networking" - direction = "INGRESS" - priority = 100 - source_tags = null - source_service_accounts = null - target_tags = null - target_service_accounts = null + rules = [ + { + name = "fw-b-cbpools-100-i-a-all-all-all-service-networking" + description = "Allow ingress from the IPs configured for service networking" + direction = "INGRESS" + priority = 100 + source_tags = null + source_service_accounts = null + target_tags = null + target_service_accounts = null + + ranges = [local.peered_ip_range] + + allow = [{ + protocol = "all" + ports = null + }] + + deny = [] + + log_config = { + metadata = "INCLUDE_ALL_METADATA" + } + }, + { + name = "allow-pool-to-nat" + description = "Allow all from worker pool to NAT gateway" + direction = "INGRESS" + priority = 1000 + source_tags = null + source_service_accounts = null + target_tags = ["nat-gateway"] + target_service_accounts = null + + ranges = ["${google_compute_global_address.worker_pool_range[0].address}/${google_compute_global_address.worker_pool_range[0].prefix_length}"] + + allow = [{ + protocol = "all" + ports = null + }] + + deny = [] + + log_config = { + metadata = "INCLUDE_ALL_METADATA" + } + }, + { + name = "allow-icmp" + description = "Allow ICMP from anywhere" + direction = "INGRESS" + priority = 65534 + source_tags = null + source_service_accounts = null + target_tags = null + target_service_accounts = null + + ranges = ["0.0.0.0/1"] + + allow = [{ + protocol = "icmp" + ports = null + }] + + deny = [] + + log_config = { + metadata = "INCLUDE_ALL_METADATA" + } + }, + { + name = "allow-ssh-ingress" + description = "Allow SSH from anywhere (0.0.0.0/1)" + direction = "INGRESS" + priority = 1000 + source_tags = null + source_service_accounts = null + target_tags = null + target_service_accounts = null + + ranges = ["0.0.0.0/1"] + + allow = [{ + protocol = "tcp" + ports = ["22"] + }] - ranges = [local.peered_ip_range] + deny = [] + + log_config = { + metadata = "INCLUDE_ALL_METADATA" + } + } + ] +} + +resource "google_compute_address" "cloud_build_nat" { + project = var.project_id + address_type = "EXTERNAL" + name = "cloud-build-nat" + network_tier = "PREMIUM" + region = "us-central1" + + depends_on = [module.peered_network] +} - allow = [{ - protocol = "all" - ports = null - }] +resource "google_compute_instance" "vm-proxy" { + project = var.project_id + name = "cloud-build-nat-vm" + machine_type = "e2-medium" + zone = "us-central1-a" - deny = [] + tags = ["direct-gateway-access", "nat-gateway"] - log_config = { - metadata = "INCLUDE_ALL_METADATA" + boot_disk { + initialize_params { + image = "debian-cloud/debian-12" } - }] + } + + network_interface { + network = local.peered_network_name + subnetwork = "sb-b-cbpools-${var.private_worker_pool.region}" + subnetwork_project = var.project_id + + } + + can_ip_forward = true + + // This script configures the VM to do IP Forwarding + metadata_startup_script = "sysctl -w net.ipv4.ip_forward=1 && iptables -t nat -A POSTROUTING -o $(ip addr show scope global | head -1 | awk -F: '{print $2}') -j MASQUERADE" + + service_account { + scopes = ["cloud-platform"] + } + + depends_on = [ + resource.google_compute_router_nat.cb-nat, + module.peered_network + ] +} + +# This route will route packets to the NAT VM +resource "google_compute_route" "through-nat" { + name = "through-nat-range1" + project = var.project_id + dest_range = "0.0.0.0/1" + network = local.peered_network_name + next_hop_instance = google_compute_instance.vm-proxy.id + priority = 10 +} + +resource "google_compute_route" "through-nat2" { + name = "through-nat-range2" + project = var.project_id + dest_range = "128.0.0.0/1" + network = local.peered_network_name + next_hop_instance = google_compute_instance.vm-proxy.id + priority = 10 +} + +# This route allow the NAT VM to reach the internet with it's external IP address + +resource "google_compute_route" "direct-to-gateway" { + name = "direct-to-gateway-range1" + project = var.project_id + dest_range = "0.0.0.0/1" + network = local.peered_network_name + next_hop_gateway = "default-internet-gateway" + tags = ["direct-gateway-access"] + priority = 5 +} + +resource "google_compute_route" "direct-to-gateway2" { + name = "direct-to-gateway-range2" + project = var.project_id + dest_range = "128.0.0.0/1" + network = local.peered_network_name + next_hop_gateway = "default-internet-gateway" + tags = ["direct-gateway-access"] + priority = 5 +} + +# Cloud Router +resource "google_compute_router" "cb-router" { + name = "cb-cloud-router" + network = local.peered_network_name + region = "us-central1" + project = var.project_id +} + +# Cloud NAT +resource "google_compute_router_nat" "cb-nat" { + project = var.project_id + name = "cb-cloud-nat" + router = google_compute_router.cb-router.name + region = google_compute_router.cb-router.region + nat_ip_allocate_option = "AUTO_ONLY" + source_subnetwork_ip_ranges_to_nat = "ALL_SUBNETWORKS_ALL_IP_RANGES" + + log_config { + enable = true + filter = "ALL" + } } diff --git a/0-bootstrap/modules/cb-private-pool/variables.tf b/0-bootstrap/modules/cb-private-pool/variables.tf index 5dfcbb6a0..38ec91c41 100644 --- a/0-bootstrap/modules/cb-private-pool/variables.tf +++ b/0-bootstrap/modules/cb-private-pool/variables.tf @@ -38,9 +38,9 @@ variable "private_worker_pool" { region = optional(string, "us-central1") disk_size_gb = optional(number, 100) machine_type = optional(string, "e2-medium") - no_external_ip = optional(bool, false) - enable_network_peering = optional(bool, false) - create_peered_network = optional(bool, false) + no_external_ip = optional(bool, true) + enable_network_peering = optional(bool, true) + create_peered_network = optional(bool, true) peered_network_id = optional(string, "") peered_network_subnet_ip = optional(string, "") peering_address = optional(string, null) @@ -124,3 +124,9 @@ EOT }) default = {} } + +variable "subnet_region" { + description = "Region where a subnet will be created in the sigle project network." + type = string + default = "us-central1" +} diff --git a/0-bootstrap/outputs.tf b/0-bootstrap/outputs.tf index 01e6fe09b..0dd7b7239 100644 --- a/0-bootstrap/outputs.tf +++ b/0-bootstrap/outputs.tf @@ -71,6 +71,11 @@ output "common_config" { } } +output "parent_id" { + description = "Parent ID service account." + value = var.parent_folder != "" ? "folder-${local.parent_id}" : "org-${var.org_id}" +} + output "required_groups" { description = "List of Google Groups created that are required by the Example Foundation steps." value = var.groups.create_required_groups == false ? tomap(var.groups.required_groups) : tomap({ for key, value in module.required_group : key => value.id }) @@ -81,6 +86,16 @@ output "optional_groups" { value = var.groups.create_optional_groups == false ? tomap(var.groups.optional_groups) : tomap({ for key, value in module.optional_group : key => value.id }) } +output "seed_project_number" { + description = "The seed project number." + value = data.google_project.seed_project.number +} + +output "cloudbuild_project_number" { + description = "The cloudbuild project number." + value = data.google_project.cloudbuild_project.number +} + /* ---------------------------------------- Specific to cloudbuild_module ---------------------------------------- */ diff --git a/0-bootstrap/sa.tf b/0-bootstrap/sa.tf index 0802d507f..e35b73cae 100644 --- a/0-bootstrap/sa.tf +++ b/0-bootstrap/sa.tf @@ -49,14 +49,18 @@ locals { "roles/resourcemanager.tagUser", "roles/cloudasset.owner", "roles/securitycenter.sourcesEditor", + "roles/serviceusage.serviceUsageConsumer", ], local.common_roles)), "env" = distinct(concat([ + "roles/accesscontextmanager.policyAdmin", "roles/resourcemanager.tagUser", "roles/assuredworkloads.admin", + "roles/serviceusage.serviceUsageConsumer", ], local.common_roles)), "net" = distinct(concat([ "roles/accesscontextmanager.policyAdmin", "roles/compute.xpnAdmin", + "roles/serviceusage.serviceUsageConsumer", ], local.common_roles)), "proj" = distinct(concat([ "roles/accesscontextmanager.policyAdmin", @@ -89,6 +93,7 @@ locals { "roles/artifactregistry.admin", "roles/compute.networkAdmin", "roles/compute.xpnAdmin", + "roles/iam.serviceAccountAdmin" ], } diff --git a/1-org/README.md b/1-org/README.md index 63d140e8a..d2ea2b0e0 100644 --- a/1-org/README.md +++ b/1-org/README.md @@ -55,7 +55,8 @@ For an overview of the architecture and the parts, see the ## Purpose -The purpose of this step is to set up top-level shared folders, networking projects, organization-level logging, and baseline security settings through organizational policies. +- Set up top-level shared folders, networking projects, organization-level logging, and baseline security settings through organizational policies. +- Set up VPC Service Controls. ## Prerequisites @@ -155,6 +156,12 @@ If required, run `terraform output cloudbuild_project_id` in the `0-bootstrap` f echo "access_context_manager_policy_id = ${ACCESS_CONTEXT_MANAGER_ID}" ``` +1. Update `envs/shared/terraform.tfvars` file with the `access_context_manager_policy_id`. + + ```bash + sed -i'' -e "s/ACCESS_CONTEXT_MANAGER_ID/${ACCESS_CONTEXT_MANAGER_ID}/" ./envs/shared/terraform.tfvars + ``` + 1. Update the `envs/shared/terraform.tfvars` file with values from your environment and 0-bootstrap step. If the previous step showed a numeric value, un-comment the variable `create_access_context_manager_access_policy = false`. See the shared folder [README.md](./envs/shared/README.md) for additional information on the values in the `terraform.tfvars` file. ```bash @@ -166,6 +173,14 @@ If required, run `terraform output cloudbuild_project_id` in the `0-bootstrap` f if [ ! -z "${ACCESS_CONTEXT_MANAGER_ID}" ]; then sed -i'' -e "s=//create_access_context_manager_access_policy=create_access_context_manager_access_policy=" ./envs/shared/terraform.tfvars; fi ``` +1. Run `terraform init` in `/envs/shared` to generate the outputs used in other steps. + + ```bash + cd envs/shared + terraform init + cd ../.. + ``` + 1. Commit changes. ```bash @@ -238,7 +253,7 @@ Create `gcp-org` folder, copy `1-org` content and Terraform wrapper script; ensu 1. Check if a Security Command Center notification with the default name, **scc-notify**, already exists. If it exists, choose a different value for the `scc_notification_name` variable in the `./envs/shared/terraform.tfvars` file. ```bash - export ORGANIZATION_ID=$(terraform -chdir="../gcp-bootstrap/" output -json common_config | jq '.org_id' --raw-output) + export ORGANIZATION_ID=$(terraform -chdir="../gcp-bootstrap/envs/shared" output -json common_config | jq '.org_id' --raw-output) gcloud scc notifications describe "scc-notify" --organization=${ORGANIZATION_ID} --location=global ``` @@ -249,10 +264,16 @@ Create `gcp-org` folder, copy `1-org` content and Terraform wrapper script; ensu echo "access_context_manager_policy_id = ${ACCESS_CONTEXT_MANAGER_ID}" ``` +1. Update `envs/shared/terraform.tfvars` file with the `access_context_manager_policy_id`. + + ```bash + sed -i'' -e "s/ACCESS_CONTEXT_MANAGER_ID/${ACCESS_CONTEXT_MANAGER_ID}/" ./envs/shared/terraform.tfvars + ``` + 1. Update the `envs/shared/terraform.tfvars` file with values from your environment and `gcp-bootstrap` step. If the previous step showed a numeric value, un-comment the variable `create_access_context_manager_access_policy = false`. See the shared folder [README.md](./envs/shared/README.md) for additional information on the values in the `terraform.tfvars` file. ```bash - export backend_bucket=$(terraform -chdir="../gcp-bootstrap/" output -raw gcs_bucket_tfstate) + export backend_bucket=$(terraform -chdir="../gcp-bootstrap/envs/shared/" output -raw gcs_bucket_tfstate) echo "remote_state_bucket = ${backend_bucket}" sed -i'' -e "s/REMOTE_STATE_BUCKET/${backend_bucket}/" ./envs/shared/terraform.tfvars @@ -267,10 +288,10 @@ To use the `validate` option of the `tf-wrapper.sh` script, follow the [instruct 1. Use `terraform output` to get the Seed project ID and the organization step Terraform service account from gcp-bootstrap output. An environment variable `GOOGLE_IMPERSONATE_SERVICE_ACCOUNT` will be set using the Terraform Service Account to enable impersonation. ```bash - export SEED_PROJECT_ID=$(terraform -chdir="../gcp-bootstrap/" output -raw seed_project_id) + export SEED_PROJECT_ID=$(terraform -chdir="../gcp-bootstrap/envs/shared/" output -raw seed_project_id) echo ${SEED_PROJECT_ID} - export GOOGLE_IMPERSONATE_SERVICE_ACCOUNT=$(terraform -chdir="../gcp-bootstrap/" output -raw organization_step_terraform_service_account_email) + export GOOGLE_IMPERSONATE_SERVICE_ACCOUNT=$(terraform -chdir="../gcp-bootstrap/envs/shared/" output -raw organization_step_terraform_service_account_email) echo ${GOOGLE_IMPERSONATE_SERVICE_ACCOUNT} ``` @@ -310,3 +331,11 @@ Before executing the next stages, unset the `GOOGLE_IMPERSONATE_SERVICE_ACCOUNT` ```bash unset GOOGLE_IMPERSONATE_SERVICE_ACCOUNT ``` + +### (Optional) Enforce VPC Service Controls + +Because enabling VPC Service Controls can be a disruptive process, this repo configures VPC Service Controls perimeters in dry run mode by default. This configuration will service traffic that crosses the security perimeter (API requests that originate from inside your perimeter communicating with external resources, or API requests from external resources communicating with resources inside your perimeter) but still allow service traffic normally. + +When you are ready to enforce VPC Service Controls, we recommend that you review the guidance at [Best practices for enabling VPC Service Controls](https://cloud.google.com/vpc-service-controls/docs/enable). After you have added the necessary exceptions and are confident that VPC Service Controls will not disrupt your intended operations, set the variable `enforce_vpcsc` under the module `service_control` to `true` and re-apply this stage. Then re-apply the 2-environments and 4-projects stage, which will inherit the new setting and include those projects inside the enforced perimeter. + +When you need to make changes to an existing enforced perimeter, you can test safely by modifying the configuration of the [dry run perimeter](https://cloud.google.com/vpc-service-controls/docs/dry-run-mode). This will log traffic denied by the dry run perimeter without impacting whether the enforced perimeter allows or denies traffic. diff --git a/1-org/envs/shared/README.md b/1-org/envs/shared/README.md index 0bb97740d..5a5d3126e 100644 --- a/1-org/envs/shared/README.md +++ b/1-org/envs/shared/README.md @@ -3,25 +3,45 @@ | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| +| access\_context\_manager\_policy\_id | The id of the default Access Context Manager policy. Can be obtained by running `gcloud access-context-manager policies list --organization YOUR_ORGANIZATION_ID --format="value(name)"`. | `string` | `""` | no | | billing\_export\_dataset\_location | The location of the dataset for billing data export. | `string` | `null` | no | | create\_access\_context\_manager\_access\_policy | Whether to create access context manager access policy. | `bool` | `true` | no | | create\_unique\_tag\_key | Creates unique organization-wide tag keys by adding a random suffix to each key. | `bool` | `false` | no | +| custom\_restricted\_services | List of custom services to be protected by the VPC-SC perimeter. If empty, all supported services (https://cloud.google.com/vpc-service-controls/docs/supported-products) will be protected. | `list(string)` | `[]` | no | +| custom\_restricted\_services\_dry\_run | List of custom services to be protected by the VPC-SC perimeter. If empty, all supported services (https://cloud.google.com/vpc-service-controls/docs/supported-products) will be protected. | `list(string)` | `[]` | no | | domains\_to\_allow | The list of domains to allow users from in IAM. Used by Domain Restricted Sharing Organization Policy. Must include the domain of the organization you are deploying the foundation. To add other domains you must also grant access to these domains to the Terraform Service Account used in the deploy. | `list(string)` | n/a | yes | +| egress\_policies | A list of all [egress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#egress-rules-reference) to use in a dry-run perimeter. Each list object has a `from` and `to` value that describes egress\_from and egress\_to.

Example: `[{ from={ identities=[], identity_type="ID_TYPE" }, to={ resources=[], operations={ "SRV_NAME"={ OP_TYPE=[] }}}}]`

Valid Values:
`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`
`SRV_NAME` = "`*`" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)
`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) |
list(object({
title = optional(string, null)
from = object({
sources = optional(object({
resources = optional(list(string), [])
access_levels = optional(list(string), [])
}), {}),
identity_type = optional(string, null)
identities = optional(list(string), null)
})
to = object({
operations = optional(map(object({
methods = optional(list(string), [])
permissions = optional(list(string), [])
})), {}),
roles = optional(list(string), null)
resources = optional(list(string), ["*"])
external_resources = optional(list(string), [])
})
}))
| `[]` | no | +| egress\_policies\_dry\_run | A list of all [egress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#egress-rules-reference) to use in a dry-run perimeter. Each list object has a `from` and `to` value that describes egress\_from and egress\_to.

Example: `[{ from={ identities=[], identity_type="ID_TYPE" }, to={ resources=[], operations={ "SRV_NAME"={ OP_TYPE=[] }}}}]`

Valid Values:
`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`
`SRV_NAME` = "`*`" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)
`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) |
list(object({
title = optional(string, null)
from = object({
sources = optional(object({
resources = optional(list(string), [])
access_levels = optional(list(string), [])
}), {}),
identity_type = optional(string, null)
identities = optional(list(string), null)
})
to = object({
operations = optional(map(object({
methods = optional(list(string), [])
permissions = optional(list(string), [])
})), {}),
roles = optional(list(string), null)
resources = optional(list(string), ["*"])
external_resources = optional(list(string), [])
})
}))
| `[]` | no | +| egress\_policies\_keys | A list of keys to use for the Terraform state. The order should correspond to var.egress\_policies and the keys must not be dynamically computed. If `null`, var.egress\_policies will be used as keys. | `list(string)` | `[]` | no | +| egress\_policies\_keys\_dry\_run | (Dry-run) A list of keys to use for the Terraform state. The order should correspond to var.egress\_policies\_dry\_run and the keys must not be dynamically computed. If `null`, var.egress\_policies\_dry\_run will be used as keys. | `list(string)` | `[]` | no | | enable\_hub\_and\_spoke | Enable Hub-and-Spoke architecture. | `bool` | `false` | no | | enable\_kms\_key\_usage\_tracking | Enable KMS centralized key usage tracking system. | `bool` | `true` | no | | enable\_scc\_resources\_in\_terraform | Create Security Command Center resources in Terraform. Security Command Center must be activated before the creation of the resources. See [Overview of activating Security Command Center](https://cloud.google.com/security-command-center/docs/activate-scc-overview) before enabling this feature. | `bool` | `false` | no | | enforce\_allowed\_worker\_pools | Whether to enforce the organization policy restriction on allowed worker pools for Cloud Build. | `bool` | `false` | no | +| envs | n/a | `map(bool)` |
{
"development": true,
"nonproduction": true,
"production": true
}
| no | | essential\_contacts\_domains\_to\_allow | The list of domains that email addresses added to Essential Contacts can have. | `list(string)` | n/a | yes | | essential\_contacts\_language | Essential Contacts preferred language for notifications, as a ISO 639-1 language code. See [Supported languages](https://cloud.google.com/resource-manager/docs/managing-notification-contacts#supported-languages) for a list of supported languages. | `string` | `"en"` | no | | folder\_deletion\_protection | Prevent Terraform from destroying or recreating the folder. | `string` | `true` | no | | gcp\_groups | Groups to grant specific roles in the Organization.
platform\_viewer: Google Workspace or Cloud Identity group that have the ability to view resource information across the Google Cloud organization.
security\_reviewer: Google Workspace or Cloud Identity group that members are part of the security team responsible for reviewing cloud security
network\_viewer: Google Workspace or Cloud Identity group that members are part of the networking team and review network configurations.
scc\_admin: Google Workspace or Cloud Identity group that can administer Security Command Center.
audit\_viewer: Google Workspace or Cloud Identity group that members are part of an audit team and view audit logs in the logging project.
global\_secrets\_admin: Google Workspace or Cloud Identity group that members are responsible for putting secrets into Secrets Management. |
object({
audit_viewer = optional(string, null)
security_reviewer = optional(string, null)
network_viewer = optional(string, null)
scc_admin = optional(string, null)
global_secrets_admin = optional(string, null)
kms_admin = optional(string, null)
})
| `{}` | no | +| ingress\_policies | A list of all [ingress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#ingress-rules-reference) to use in a dry-run perimeter. Each list object has a `from` and `to` value that describes ingress\_from and ingress\_to.

Example: `[{ from={ sources={ resources=[], access_levels=[] }, identities=[], identity_type="ID_TYPE" }, to={ resources=[], operations={ "SRV_NAME"={ OP_TYPE=[] }}}}]`

Valid Values:
`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`
`SRV_NAME` = "`*`" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)
`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) |
list(object({
title = optional(string, null)
from = object({
sources = optional(object({
resources = optional(list(string), [])
access_levels = optional(list(string), [])
}), {}),
identity_type = optional(string, null)
identities = optional(list(string), null)
})
to = object({
operations = optional(map(object({
methods = optional(list(string), [])
permissions = optional(list(string), [])
})), {}),
roles = optional(list(string), null)
resources = optional(list(string), ["*"])
})
}))
| `[]` | no | +| ingress\_policies\_dry\_run | A list of all [ingress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#ingress-rules-reference) to use in a dry-run perimeter. Each list object has a `from` and `to` value that describes ingress\_from and ingress\_to.

Example: `[{ from={ sources={ resources=[], access_levels=[] }, identities=[], identity_type="ID_TYPE" }, to={ resources=[], operations={ "SRV_NAME"={ OP_TYPE=[] }}}}]`

Valid Values:
`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`
`SRV_NAME` = "`*`" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)
`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) |
list(object({
title = optional(string, null)
from = object({
sources = optional(object({
resources = optional(list(string), [])
access_levels = optional(list(string), [])
}), {}),
identity_type = optional(string, null)
identities = optional(list(string), null)
})
to = object({
operations = optional(map(object({
methods = optional(list(string), [])
permissions = optional(list(string), [])
})), {}),
roles = optional(list(string), null)
resources = optional(list(string), ["*"])
})
}))
| `[]` | no | +| ingress\_policies\_keys | A list of keys to use for the Terraform state. The order should correspond to var.ingress\_policies and the keys must not be dynamically computed. If `null`, var.ingress\_policies will be used as keys. | `list(string)` | `[]` | no | +| ingress\_policies\_keys\_dry\_run | (Dry-run) A list of keys to use for the Terraform state. The order should correspond to var.ingress\_policies\_dry\_run and the keys must not be dynamically computed. If `null`, var.ingress\_policies\_dry\_run will be used as keys. | `list(string)` | `[]` | no | | log\_export\_storage\_force\_destroy | (Optional) If set to true, delete all contents when destroying the resource; otherwise, destroying the resource will fail if contents are present. | `bool` | `false` | no | | log\_export\_storage\_location | The location of the storage bucket used to export logs. | `string` | `null` | no | | log\_export\_storage\_retention\_policy | Configuration of the bucket's data retention policy for how long objects in the bucket should be retained. |
object({
is_locked = bool
retention_period_days = number
})
| `null` | no | | log\_export\_storage\_versioning | (Optional) Toggles bucket versioning, ability to retain a non-current object version when the live object version gets replaced or deleted. | `bool` | `false` | no | +| perimeter\_additional\_members | The list of additional members to be added to the enforced perimeter access level members list. To be able to see the resources protected by the VPC Service Controls in the perimeter, add your user in this list. Entries must be in the standard GCP form: `user:email@example.com` or `serviceAccount:my-service-account@example.com`. | `list(string)` | `[]` | no | +| perimeter\_additional\_members\_dry\_run | The list of additional members to be added to the dry-run perimeter access level members list. To be able to see the resources protected by the VPC Service Controls in the perimeter, add your user in this list. Entries must be in the standard GCP form: `user:email@example.com` or `serviceAccount:my-service-account@example.com`. | `list(string)` | `[]` | no | | project\_budget | Budget configuration for projects.
budget\_amount: The amount to use as the budget.
alert\_spent\_percents: A list of percentages of the budget to alert on when threshold is exceeded.
alert\_pubsub\_topic: The name of the Cloud Pub/Sub topic where budget related messages will be published, in the form of `projects/{project_id}/topics/{topic_id}`.
alert\_spend\_basis: The type of basis used to determine if spend has passed the threshold. Possible choices are `CURRENT_SPEND` or `FORECASTED_SPEND` (default). |
object({
net_hub_budget_amount = optional(number, 1000)
net_hub_alert_spent_percents = optional(list(number), [1.2])
net_hub_alert_pubsub_topic = optional(string, null)
net_hub_budget_alert_spend_basis = optional(string, "FORECASTED_SPEND")
shared_network_budget_amount = optional(number, 1000)
shared_network_alert_spent_percents = optional(list(number), [1.2])
shared_network_alert_pubsub_topic = optional(string, null)
shared_network_budget_alert_spend_basis = optional(string, "FORECASTED_SPEND")
interconnect_budget_amount = optional(number, 1000)
interconnect_alert_spent_percents = optional(list(number), [1.2])
interconnect_alert_pubsub_topic = optional(string, null)
interconnect_budget_alert_spend_basis = optional(string, "FORECASTED_SPEND")
org_secrets_budget_amount = optional(number, 1000)
org_secrets_alert_spent_percents = optional(list(number), [1.2])
org_secrets_alert_pubsub_topic = optional(string, null)
org_secrets_budget_alert_spend_basis = optional(string, "FORECASTED_SPEND")
org_billing_export_budget_amount = optional(number, 1000)
org_billing_export_alert_spent_percents = optional(list(number), [1.2])
org_billing_export_alert_pubsub_topic = optional(string, null)
org_billing_export_budget_alert_spend_basis = optional(string, "FORECASTED_SPEND")
org_audit_logs_budget_amount = optional(number, 1000)
org_audit_logs_alert_spent_percents = optional(list(number), [1.2])
org_audit_logs_alert_pubsub_topic = optional(string, null)
org_audit_logs_budget_alert_spend_basis = optional(string, "FORECASTED_SPEND")
common_kms_budget_amount = optional(number, 1000)
common_kms_alert_spent_percents = optional(list(number), [1.2])
common_kms_alert_pubsub_topic = optional(string, null)
common_kms_budget_alert_spend_basis = optional(string, "FORECASTED_SPEND")
scc_notifications_budget_amount = optional(number, 1000)
scc_notifications_alert_spent_percents = optional(list(number), [1.2])
scc_notifications_alert_pubsub_topic = optional(string, null)
scc_notifications_budget_alert_spend_basis = optional(string, "FORECASTED_SPEND")
})
| `{}` | no | | project\_deletion\_policy | The deletion policy for the project created. | `string` | `"PREVENT"` | no | | remote\_state\_bucket | Backend bucket to load Terraform Remote State Data from previous steps. | `string` | n/a | yes | +| required\_egress\_rules\_app\_infra | Required egress rule app infra enforced mode. | `bool` | `false` | no | +| required\_egress\_rules\_app\_infra\_dry\_run | Required egress rule app infra dry run mode. | `bool` | `false` | no | +| required\_ingress\_rules\_app\_infra | Required ingress rule app infra enforced mode. | `bool` | `false` | no | +| required\_ingress\_rules\_app\_infra\_dry\_run | Required ingress rule app infra dry run mode. | `bool` | `false` | no | +| resources | A list of GCP resources that are inside of the service perimeter. Currently only projects and VPC networks are allowed. | `list(string)` | `[]` | no | +| resources\_dry\_run | A list of GCP resources that are inside of the service perimeter. Currently only projects and VPC networks are allowed. If set, a dry-run policy will be set. | `list(string)` | `[]` | no | | scc\_notification\_filter | Filter used to create the Security Command Center Notification, you can see more details on how to create filters in https://cloud.google.com/security-command-center/docs/how-to-api-filter-notifications#create-filter | `string` | `"state = \"ACTIVE\""` | no | | scc\_notification\_name | Name of the Security Command Center Notification. It must be unique in the organization. Run `gcloud scc notifications describe --organization=org_id --location=global` to check if it already exists. | `string` | n/a | yes | | tfc\_org\_name | Name of the TFC organization | `string` | `""` | no | @@ -30,6 +50,9 @@ | Name | Description | |------|-------------| +| access\_context\_manager\_policy\_id | Access Context Manager Policy ID. | +| access\_level\_name | Access context manager access level name | +| access\_level\_name\_dry\_run | Access context manager access level name for the dry-run perimeter | | billing\_sink\_names | The name of the sinks under billing account level. | | cai\_monitoring\_artifact\_registry | CAI Monitoring Cloud Function Artifact Registry name. | | cai\_monitoring\_asset\_feed | CAI Monitoring Cloud Function Organization Asset Feed name. | @@ -38,8 +61,12 @@ | common\_folder\_name | The common folder name | | common\_kms\_project\_id | The org Cloud Key Management Service (KMS) project ID | | domains\_to\_allow | The list of domains to allow users from in IAM. | +| enforce\_vpcsc | The mode of VPC Service Controls. | | interconnect\_project\_id | The Dedicated Interconnect project ID | | interconnect\_project\_number | The Dedicated Interconnect project number | +| internal\_project\_log\_export | The service account that logging uses to write log entries to the destination. | +| log\_export | The service account that logging uses to write log entries to the destination. | +| log\_export\_billing | The service account that logging uses to write log entries to the destination. | | logs\_export\_project\_linked\_dataset\_name | The resource name of the Log Bucket linked BigQuery dataset for the project destination. | | logs\_export\_project\_logbucket\_name | The resource name for the Log Bucket created for the project destination. | | logs\_export\_pubsub\_topic | The Pub/Sub topic for destination of log exports | @@ -55,6 +82,7 @@ | parent\_resource\_type | The parent resource type | | scc\_notification\_name | Name of SCC Notification | | scc\_notifications\_project\_id | The SCC notifications project ID | +| service\_perimeter\_name | Access context manager service perimeter name | | shared\_vpc\_projects | Shared VPC Projects info grouped by environment (development, nonproduction, production). | | tags | Tag Values to be applied on next steps. | diff --git a/1-org/envs/shared/folders.tf b/1-org/envs/shared/folders.tf index 90c69454a..488ca6430 100644 --- a/1-org/envs/shared/folders.tf +++ b/1-org/envs/shared/folders.tf @@ -19,13 +19,13 @@ *****************************************/ resource "google_folder" "common" { - display_name = "${local.folder_prefix}-common" - parent = local.parent - # deletion_protection = var.folder_deletion_protection // uncommnet after updating "GoogleCloudPlatform/cloud-functions/google" to provider v6 + display_name = "${local.folder_prefix}-common" + parent = local.parent + deletion_protection = var.folder_deletion_protection // uncommnet after updating "GoogleCloudPlatform/cloud-functions/google" to provider v6 } resource "google_folder" "network" { - display_name = "${local.folder_prefix}-network" - parent = local.parent - # deletion_protection = var.folder_deletion_protection // uncommnet after updating "GoogleCloudPlatform/cloud-functions/google" to provider v6 + display_name = "${local.folder_prefix}-network" + parent = local.parent + deletion_protection = var.folder_deletion_protection // uncommnet after updating "GoogleCloudPlatform/cloud-functions/google" to provider v6 } diff --git a/1-org/envs/shared/org_policy.tf b/1-org/envs/shared/org_policy.tf index 08ea8acda..2aa44c878 100644 --- a/1-org/envs/shared/org_policy.tf +++ b/1-org/envs/shared/org_policy.tf @@ -41,7 +41,8 @@ locals { "storage.publicAccessPrevention" ]) - private_pools = [local.cloud_build_private_worker_pool_id] + private_pools = [local.cloud_build_private_worker_pool_id] + access_context_manager_policy_id = var.create_access_context_manager_access_policy ? google_access_context_manager_access_policy.access_policy[0].id : var.access_context_manager_policy_id } module "organization_policies_type_boolean" { diff --git a/1-org/envs/shared/outputs.tf b/1-org/envs/shared/outputs.tf index df9e55da2..6ac9d6dab 100644 --- a/1-org/envs/shared/outputs.tf +++ b/1-org/envs/shared/outputs.tf @@ -148,3 +148,43 @@ output "cai_monitoring_topic" { value = try(module.cai_monitoring[0].topic_name, null) description = "CAI Monitoring Cloud Function Pub/Sub Topic name." } + +output "enforce_vpcsc" { + value = module.service_control.enforce_vpcsc + description = "The mode of VPC Service Controls." +} + +output "service_perimeter_name" { + value = module.service_control.service_perimeter_name + description = "Access context manager service perimeter name" +} + +output "access_level_name" { + value = module.service_control.access_level_name + description = "Access context manager access level name" +} + +output "access_level_name_dry_run" { + value = module.service_control.access_level_name_dry_run + description = "Access context manager access level name for the dry-run perimeter" +} + +output "access_context_manager_policy_id" { + value = local.access_context_manager_policy_id + description = "Access Context Manager Policy ID." +} + +output "log_export_billing" { + description = "The service account that logging uses to write log entries to the destination." + value = module.logs_export.log_export_billing +} + +output "log_export" { + description = "The service account that logging uses to write log entries to the destination." + value = module.logs_export.log_export +} + +output "internal_project_log_export" { + description = "The service account that logging uses to write log entries to the destination." + value = module.logs_export.internal_project_log_export +} diff --git a/1-org/envs/shared/projects.tf b/1-org/envs/shared/projects.tf index 5798fa6b1..dd7e4095c 100644 --- a/1-org/envs/shared/projects.tf +++ b/1-org/envs/shared/projects.tf @@ -90,6 +90,7 @@ module "org_billing_export" { env_code = "c" vpc = "none" } + budget_alert_pubsub_topic = var.project_budget.org_billing_export_alert_pubsub_topic budget_alert_spent_percents = var.project_budget.org_billing_export_alert_spent_percents budget_amount = var.project_budget.org_billing_export_budget_amount @@ -149,6 +150,7 @@ module "org_secrets" { deletion_policy = var.project_deletion_policy activate_apis = ["logging.googleapis.com", "secretmanager.googleapis.com", "billingbudgets.googleapis.com"] + labels = { environment = "common" application_name = "org-secrets" @@ -193,6 +195,7 @@ module "interconnect" { env_code = "net" vpc = "none" } + budget_alert_pubsub_topic = var.project_budget.interconnect_alert_pubsub_topic budget_alert_spent_percents = var.project_budget.interconnect_alert_spent_percents budget_amount = var.project_budget.interconnect_budget_amount @@ -227,6 +230,7 @@ module "scc_notifications" { env_code = "c" vpc = "none" } + budget_alert_pubsub_topic = var.project_budget.scc_notifications_alert_pubsub_topic budget_alert_spent_percents = var.project_budget.scc_notifications_alert_spent_percents budget_amount = var.project_budget.scc_notifications_budget_amount @@ -270,6 +274,7 @@ module "network_hub" { env_code = "net" vpc = "svpc" } + budget_alert_pubsub_topic = var.project_budget.net_hub_alert_pubsub_topic budget_alert_spent_percents = var.project_budget.net_hub_alert_spent_percents budget_amount = var.project_budget.net_hub_budget_amount @@ -313,3 +318,18 @@ resource "google_project_iam_member" "network_sa" { role = each.key member = "serviceAccount:${local.networks_step_terraform_service_account_email}" } + +resource "time_sleep" "wait_projects" { + create_duration = "30s" + + depends_on = [ + module.org_audit_logs, + module.org_billing_export, + module.common_kms, + module.org_secrets, + module.interconnect, + module.scc_notifications, + module.network_hub, + module.environment_network + ] +} diff --git a/1-org/envs/shared/remote.tf b/1-org/envs/shared/remote.tf index 9b54670c8..3cbe09888 100644 --- a/1-org/envs/shared/remote.tf +++ b/1-org/envs/shared/remote.tf @@ -31,6 +31,31 @@ locals { bootstrap_folder_name = data.terraform_remote_state.bootstrap.outputs.common_config.bootstrap_folder_name cloud_build_private_worker_pool_id = try(data.terraform_remote_state.bootstrap.outputs.cloud_build_private_worker_pool_id, "") required_groups = data.terraform_remote_state.bootstrap.outputs.required_groups + organization_service_account = data.terraform_remote_state.bootstrap.outputs.organization_step_terraform_service_account_email + networks_service_account = data.terraform_remote_state.bootstrap.outputs.networks_step_terraform_service_account_email + projects_service_account = data.terraform_remote_state.bootstrap.outputs.projects_step_terraform_service_account_email + environment_service_account = data.terraform_remote_state.bootstrap.outputs.environment_step_terraform_service_account_email + cloudbuild_project_number = data.terraform_remote_state.bootstrap.outputs.cloudbuild_project_number + seed_project_id = data.terraform_remote_state.bootstrap.outputs.seed_project_id + seed_project_number = data.terraform_remote_state.bootstrap.outputs.seed_project_number + parent_id = data.terraform_remote_state.bootstrap.outputs.parent_id + projects_gcs_bucket_tfstate = data.terraform_remote_state.bootstrap.outputs.projects_gcs_bucket_tfstate + peering_projects_numbers = compact([for s in data.terraform_remote_state.projects_env : try(s.outputs.peering_project_number, null)]) + shared_vpc_project_numbers = compact([for s in data.terraform_remote_state.projects_env : try(s.outputs.shared_vpc_project_number, null)]) + app_infra_project_id = try(data.terraform_remote_state.projects_app_infra[0].outputs.cloudbuild_project_id, null) + app_infra_project_number = try(data.terraform_remote_state.projects_app_infra[0].outputs.cloudbuild_project_number, null) + + app_infra_pipeline_identity = local.app_infra_project_number != null ? "serviceAccount:${local.app_infra_project_number}@cloudbuild.gserviceaccount.com" : null + app_infra_pipeline_source_projects = local.app_infra_project_number != null ? ["projects/${local.app_infra_project_number}"] : [] + app_infra_targets = distinct(concat( + [for n in local.shared_vpc_project_numbers : "projects/${n}"], + [for n in local.peering_projects_numbers : "projects/${n}"] + )) + app_infra_cicd_identity = ( + local.app_infra_project_id != null + ? "serviceAccount:sa-tf-cb-bu1-example-app@${local.app_infra_project_id}.iam.gserviceaccount.com" + : null + ) } data "terraform_remote_state" "bootstrap" { @@ -41,3 +66,25 @@ data "terraform_remote_state" "bootstrap" { prefix = "terraform/bootstrap/state" } } + +data "terraform_remote_state" "projects_env" { + backend = "gcs" + + for_each = (var.required_egress_rules_app_infra_dry_run && var.required_ingress_rules_app_infra_dry_run) || (var.required_egress_rules_app_infra && var.required_ingress_rules_app_infra) ? var.envs : {} + + config = { + bucket = local.projects_gcs_bucket_tfstate + prefix = "terraform/projects/business_unit_1/${each.key}" + } +} + +data "terraform_remote_state" "projects_app_infra" { + backend = "gcs" + + count = (var.required_egress_rules_app_infra_dry_run && var.required_ingress_rules_app_infra_dry_run) || (var.required_egress_rules_app_infra && var.required_ingress_rules_app_infra) ? 1 : 0 + + config = { + bucket = local.projects_gcs_bucket_tfstate + prefix = "terraform/projects/business_unit_1/shared" + } +} diff --git a/1-org/envs/shared/service_control.tf b/1-org/envs/shared/service_control.tf new file mode 100644 index 000000000..a9b3c2150 --- /dev/null +++ b/1-org/envs/shared/service_control.tf @@ -0,0 +1,904 @@ +/** + * Copyright 2023 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. + */ + +/****************************************** + Shared VPC +*****************************************/ + +locals { + supported_restricted_service = [ + "serviceusage.googleapis.com", + "essentialcontacts.googleapis.com", + "accessapproval.googleapis.com", + "adsdatahub.googleapis.com", + "aiplatform.googleapis.com", + "alloydb.googleapis.com", + "analyticshub.googleapis.com", + "apigee.googleapis.com", + "apigeeconnect.googleapis.com", + "artifactregistry.googleapis.com", + "assuredworkloads.googleapis.com", + "automl.googleapis.com", + "baremetalsolution.googleapis.com", + "batch.googleapis.com", + "bigquery.googleapis.com", + "bigquerydatapolicy.googleapis.com", + "bigquerydatatransfer.googleapis.com", + "bigquerymigration.googleapis.com", + "bigqueryreservation.googleapis.com", + "bigtable.googleapis.com", + "binaryauthorization.googleapis.com", + "cloud.googleapis.com", + "cloudasset.googleapis.com", + "cloudbuild.googleapis.com", + "clouddebugger.googleapis.com", + "clouddeploy.googleapis.com", + "clouderrorreporting.googleapis.com", + "cloudfunctions.googleapis.com", + "cloudkms.googleapis.com", + "cloudprofiler.googleapis.com", + "cloudresourcemanager.googleapis.com", + "cloudscheduler.googleapis.com", + "cloudsearch.googleapis.com", + "cloudtrace.googleapis.com", + "composer.googleapis.com", + "compute.googleapis.com", + "connectgateway.googleapis.com", + "contactcenterinsights.googleapis.com", + "container.googleapis.com", + "containeranalysis.googleapis.com", + "containerfilesystem.googleapis.com", + "containerregistry.googleapis.com", + "containerthreatdetection.googleapis.com", + "datacatalog.googleapis.com", + "dataflow.googleapis.com", + "datafusion.googleapis.com", + "datamigration.googleapis.com", + "dataplex.googleapis.com", + "dataproc.googleapis.com", + "datastream.googleapis.com", + "dialogflow.googleapis.com", + "dlp.googleapis.com", + "dns.googleapis.com", + "documentai.googleapis.com", + "domains.googleapis.com", + "eventarc.googleapis.com", + "file.googleapis.com", + "firebaseappcheck.googleapis.com", + "firebaserules.googleapis.com", + "firestore.googleapis.com", + "gameservices.googleapis.com", + "gkebackup.googleapis.com", + "gkeconnect.googleapis.com", + "gkehub.googleapis.com", + "healthcare.googleapis.com", + "iam.googleapis.com", + "iamcredentials.googleapis.com", + "iaptunnel.googleapis.com", + "ids.googleapis.com", + "integrations.googleapis.com", + "kmsinventory.googleapis.com", + "krmapihosting.googleapis.com", + "language.googleapis.com", + "lifesciences.googleapis.com", + "logging.googleapis.com", + "managedidentities.googleapis.com", + "memcache.googleapis.com", + "meshca.googleapis.com", + "meshconfig.googleapis.com", + "metastore.googleapis.com", + "ml.googleapis.com", + "monitoring.googleapis.com", + "networkconnectivity.googleapis.com", + "networkmanagement.googleapis.com", + "networksecurity.googleapis.com", + "networkservices.googleapis.com", + "notebooks.googleapis.com", + "opsconfigmonitoring.googleapis.com", + "orgpolicy.googleapis.com", + "osconfig.googleapis.com", + "oslogin.googleapis.com", + "privateca.googleapis.com", + "pubsub.googleapis.com", + "pubsublite.googleapis.com", + "recaptchaenterprise.googleapis.com", + "recommender.googleapis.com", + "redis.googleapis.com", + "retail.googleapis.com", + "run.googleapis.com", + "secretmanager.googleapis.com", + "servicecontrol.googleapis.com", + "servicedirectory.googleapis.com", + "spanner.googleapis.com", + "speakerid.googleapis.com", + "speech.googleapis.com", + "sqladmin.googleapis.com", + "storage.googleapis.com", + "storagetransfer.googleapis.com", + "sts.googleapis.com", + "texttospeech.googleapis.com", + "timeseriesinsights.googleapis.com", + "tpu.googleapis.com", + "trafficdirector.googleapis.com", + "transcoder.googleapis.com", + "translate.googleapis.com", + "videointelligence.googleapis.com", + "vision.googleapis.com", + "visionai.googleapis.com", + "vmmigration.googleapis.com", + "vpcaccess.googleapis.com", + "webrisk.googleapis.com", + "workflows.googleapis.com", + "workstations.googleapis.com", + "confidentialcomputing.googleapis.com", + ] + + restricted_services = length(var.custom_restricted_services) != 0 ? var.custom_restricted_services : local.supported_restricted_service + restricted_services_dry_run = length(var.custom_restricted_services_dry_run) != 0 ? var.custom_restricted_services : local.supported_restricted_service + + access_level_name = module.service_control.access_level_name + access_level_dry_run_name = module.service_control.access_level_name_dry_run + + shared_vpc_projects_numbers = [ + for v in values({ + for k, m in module.environment_network : + k => m.shared_vpc_project_number + }) : tostring(v) + ] + + projects = var.enable_hub_and_spoke ? (concat([ + local.seed_project_number, + module.org_audit_logs.project_number, + module.org_billing_export.project_number, + module.common_kms.project_number, + module.org_secrets.project_number, + module.interconnect.project_number, + module.network_hub[0].project_number, + module.scc_notifications.project_number, + ], local.shared_vpc_projects_numbers)) : (concat([ + local.seed_project_number, + module.org_audit_logs.project_number, + module.org_billing_export.project_number, + module.common_kms.project_number, + module.org_secrets.project_number, + module.interconnect.project_number, + module.scc_notifications.project_number, + ], local.shared_vpc_projects_numbers)) + + projects_dry_run = var.enable_hub_and_spoke ? (concat([ + local.seed_project_number, + module.org_audit_logs.project_number, + module.org_billing_export.project_number, + module.common_kms.project_number, + module.org_secrets.project_number, + module.interconnect.project_number, + module.network_hub[0].project_number, + module.scc_notifications.project_number, + ], local.shared_vpc_projects_numbers)) : (concat([ + local.seed_project_number, + module.org_audit_logs.project_number, + module.org_billing_export.project_number, + module.common_kms.project_number, + module.org_secrets.project_number, + module.interconnect.project_number, + module.scc_notifications.project_number, + ], local.shared_vpc_projects_numbers)) + + project_keys = var.enable_hub_and_spoke ? [ + "prj-b-seed", + "prj-org-audit", + "prj-org-billing", + "prj-org-kms", + "prj-org-secrets", + "prj-org-interconnect", + "prj-org-scc", + "prj-net-hub-svpc", + "prj-net-p-svpc", + "prj-net-d-svpc", + "prj-net-n-svpc", + ] : [ + "prj-b-seed", + "prj-org-audit", + "prj-org-billing", + "prj-org-kms", + "prj-org-secrets", + "prj-org-interconnect", + "prj-org-scc", + "prj-net-p-svpc", + "prj-net-d-svpc", + "prj-net-n-svpc", + ] + + project_keys_dry_run = var.enable_hub_and_spoke ? [ + "prj-b-seed", + "prj-org-audit", + "prj-org-billing", + "prj-org-kms", + "prj-org-secrets", + "prj-org-interconnect", + "prj-org-scc", + "prj-net-hub-svpc", + "prj-net-p-svpc", + "prj-net-d-svpc", + "prj-net-n-svpc", + ] : [ + "prj-b-seed", + "prj-org-audit", + "prj-org-billing", + "prj-org-kms", + "prj-org-secrets", + "prj-org-interconnect", + "prj-org-scc", + "prj-net-p-svpc", + "prj-net-d-svpc", + "prj-net-n-svpc", + ] + + projects_map = zipmap( + local.project_keys, + [for p in local.projects : "${p}"] + ) + + projects_map_dry_run = zipmap( + local.project_keys_dry_run, + [for p in local.projects_dry_run : "${p}"] + ) + + base_ingress_keys = [ + "billing_sa_to_prj", + "sinks_sa_to_logs", + "service_cicd_to_seed", + "cicd_to_seed", + ] + + app_infra_ingress_keys_dry_run = [ + "cicd_to_app_infra", + "cicd_to_seed_app_infra", + "cicd_to_net_env", + ] + + scc_ingress_key_dry_run = "cai_monitoring_to_scc" + + app_infra_ingress_keys = [ + "cicd_to_app_infra", + "cicd_to_seed_app_infra", + "cicd_to_net_env", + ] + + scc_ingress_key = "cai_monitoring_to_scc" + + ingress_policies_keys_dry_run = concat( + local.base_ingress_keys, + var.required_ingress_rules_app_infra_dry_run ? local.app_infra_ingress_keys_dry_run : [], + var.enable_scc_resources_in_terraform ? [local.scc_ingress_key_dry_run] : [], + var.ingress_policies_keys_dry_run + ) + + ingress_policies_keys = concat( + local.base_ingress_keys, + var.required_ingress_rules_app_infra ? local.app_infra_ingress_keys : [], + var.enable_scc_resources_in_terraform ? [local.scc_ingress_key] : [], + var.ingress_policies_keys + ) + + egress_policies_keys_dry_run = var.required_egress_rules_app_infra_dry_run ? concat(["seed_to_cicd", "org_sa_to_scc", "app_infra_to_cicd"], var.egress_policies_keys_dry_run) : concat(["seed_to_cicd", "org_sa_to_scc"], var.egress_policies_keys_dry_run) + egress_policies_keys = var.required_egress_rules_app_infra ? concat(["seed_to_cicd", "org_sa_to_scc", "app_infra_to_cicd"], var.egress_policies_keys) : concat(["seed_to_cicd", "org_sa_to_scc"], var.egress_policies_keys) + app_infra_targets_sorted = sort(local.app_infra_targets) + app_infra_to_resources = local.app_infra_project_number != null ? ["projects/${local.app_infra_project_number}"] : [] + + required_egress_rules_dry_run = [ + { + title = "ER seed -> cicd" + from = { + identities = [ + "serviceAccount:${local.cloudbuild_project_number}@cloudbuild.gserviceaccount.com", + ] + sources = { + resources = [ + "projects/${local.seed_project_number}" + ] + } + } + to = { + resources = [ + "projects/${local.cloudbuild_project_number}" + ] + operations = { + "cloudbuild.googleapis.com" = { + methods = ["*"] + } + } + } + }, + { + title = "ER seed -> scc" + from = { + identities = [ + "serviceAccount:${local.organization_service_account}", + ] + sources = { + resources = [ + "projects/${local.cloudbuild_project_number}" + ] + } + } + to = { + resources = [ + "projects/${module.scc_notifications.project_number}" + ] + operations = { + "cloudasset.googleapis.com" = { + methods = ["*"] + } + } + } + }, + ] + + required_egress_rules_app_infra_dry_run = [ + { + title = "ER app infra -> cicd" + from = { + identities = compact([local.app_infra_pipeline_identity]) + sources = { + resources = local.app_infra_pipeline_source_projects + } + } + to = { + resources = [ + "projects/${local.cloudbuild_project_number}" + ] + operations = { + "cloudbuild.googleapis.com" = { + methods = ["*"] + } + } + } + }, + ] + + required_ingress_rules_dry_run = [ + { + title = "IR billing" + from = { + identities = [ + "serviceAccount:billing-export-bigquery@system.gserviceaccount.com", + ] + sources = { + access_levels = ["*"] + } + } + to = { + resources = [ + "projects/${module.org_billing_export.project_number}" + ] + operations = { + "logging.googleapis.com" = { + methods = ["*"] + } + } + } + }, + { + title = "IR sinks" + from = { + identities = [ + "serviceAccount:service-${local.parent_id}@gcp-sa-logging.iam.gserviceaccount.com", + "serviceAccount:service-b-${local.billing_account}@gcp-sa-logging.iam.gserviceaccount.com", + ] + sources = { + access_levels = ["*"] + } + } + to = { + resources = [ + "projects/${module.org_audit_logs.project_number}" + ] + operations = { + "logging.googleapis.com" = { + methods = ["*"] + } + "pubsub.googleapis.com" = { + methods = ["*"] + } + "storage.googleapis.com" = { + methods = ["*"] + } + } + } + }, + { + title = "IR service cicd -> seed" + from = { + identities = [ + "serviceAccount:service-${local.cloudbuild_project_number}@gcp-sa-cloudbuild.iam.gserviceaccount.com", + ] + sources = { + resources = [ + "projects/${local.cloudbuild_project_number}" + ] + } + } + to = { + resources = [ + "projects/${local.seed_project_number}" + ] + operations = { + "iam.googleapis.com" = { + methods = ["*"] + } + "storage.googleapis.com" = { + methods = ["*"] + } + } + } + }, + { + title = "IR cicd -> seed" + from = { + identities = [ + "serviceAccount:${local.cloudbuild_project_number}@cloudbuild.gserviceaccount.com", + ] + sources = { + resources = [ + "projects/${local.cloudbuild_project_number}" + ] + } + } + to = { + resources = [ + "projects/${local.seed_project_number}" + ] + operations = { + "cloudbuild.googleapis.com" = { + methods = ["*"] + } + } + } + }, + ] + + required_ingress_rule_scc_dry_run = [ + { + title = "CAI -> SCC" + from = { + identities = [ + try("serviceAccount:${google_service_account.cai_monitoring_builder[0].email}", null) + ] + sources = { + access_levels = ["*"] + } + } + to = { + resources = [ + "projects/${module.scc_notifications.project_number}" + ] + operations = { + "logging.googleapis.com" = { + methods = ["*"] + } + "artifactregistry.googleapis.com" = { + methods = ["*"] + } + "storage.googleapis.com" = { + methods = ["*"] + } + } + } + }, + ] + + required_ingress_rules_app_infra_dry_run = [ + { + title = "IR cicd -> app infra" + from = { + identities = compact([local.app_infra_cicd_identity]) + sources = { + resources = [ + "projects/${local.cloudbuild_project_number}" + ] + } + } + to = { + resources = local.app_infra_to_resources + operations = { + "storage.googleapis.com" = { + methods = ["*"] + } + "logging.googleapis.com" = { + methods = ["*"] + } + "iamcredentials.googleapis.com" = { + methods = ["*"] + } + } + } + }, + { + title = "IR app infra -> seed" + from = { + identities = compact([local.app_infra_cicd_identity]) + sources = { + resources = [ + "projects/${local.cloudbuild_project_number}" + ] + } + } + to = { + resources = [ + "projects/${local.seed_project_number}" + ] + operations = { + "storage.googleapis.com" = { + methods = ["*"] + } + } + } + }, + { + title = "IR app infra -> prjs" + from = { + identities = compact([local.app_infra_cicd_identity]) + sources = { + resources = [ + "projects/${local.cloudbuild_project_number}" + ] + } + } + to = { + resources = local.app_infra_targets_sorted + operations = { + "iam.googleapis.com" = { + methods = ["*"] + } + "compute.googleapis.com" = { + methods = ["*"] + } + } + } + }, + ] + + required_ingress_rules = [ + { + title = "IR billing" + from = { + identities = [ + "serviceAccount:billing-export-bigquery@system.gserviceaccount.com", + ] + sources = { + access_levels = ["*"] + } + } + to = { + resources = [ + "projects/${module.org_billing_export.project_number}" + ] + operations = { + "logging.googleapis.com" = { + methods = ["*"] + } + } + } + }, + { + title = "IR sinks" + from = { + identities = [ + "serviceAccount:service-${local.parent_id}@gcp-sa-logging.iam.gserviceaccount.com", + "serviceAccount:service-b-${local.billing_account}@gcp-sa-logging.iam.gserviceaccount.com", + ] + sources = { + access_levels = ["*"] + } + } + to = { + resources = [ + "projects/${module.org_audit_logs.project_number}" + ] + operations = { + "logging.googleapis.com" = { + methods = ["*"] + } + "pubsub.googleapis.com" = { + methods = ["*"] + } + "storage.googleapis.com" = { + methods = ["*"] + } + } + } + }, + { + title = "IR service cicd -> seed" + from = { + identities = [ + "serviceAccount:service-${local.cloudbuild_project_number}@gcp-sa-cloudbuild.iam.gserviceaccount.com", + ] + sources = { + resources = [ + "projects/${local.cloudbuild_project_number}" + ] + } + } + to = { + resources = [ + "projects/${local.seed_project_number}" + ] + operations = { + "iam.googleapis.com" = { + methods = ["*"] + } + "storage.googleapis.com" = { + methods = ["*"] + } + } + } + }, + { + title = "IR cicd -> seed" + from = { + identities = [ + "serviceAccount:${local.cloudbuild_project_number}@cloudbuild.gserviceaccount.com" + ] + sources = { + resources = [ + "projects/${local.cloudbuild_project_number}" + ] + } + } + to = { + resources = [ + "projects/${local.seed_project_number}" + ] + operations = { + "cloudbuild.googleapis.com" = { methods = ["*"] } + } + } + }, + ] + + required_ingress_rule_scc = [ + { + title = "CAI -> SCC" + from = { + identities = [ + try("serviceAccount:${google_service_account.cai_monitoring_builder[0].email}", null) + ] + sources = { access_levels = ["*"] } + } + to = { + resources = [ + "projects/${module.scc_notifications.project_number}" + ] + operations = { + "logging.googleapis.com" = { + methods = ["*"] + } + "artifactregistry.googleapis.com" = { + methods = ["*"] + } + "storage.googleapis.com" = { + methods = ["*"] + } + } + } + }, + ] + + required_ingress_rules_app_infra = [ + { + title = "IR cicd -> app infra" + from = { + identities = compact([local.app_infra_cicd_identity]) + sources = { + resources = [ + "projects/${local.cloudbuild_project_number}" + ] + } + } + to = { + resources = local.app_infra_to_resources + operations = { + "storage.googleapis.com" = { + methods = ["*"] + } + "logging.googleapis.com" = { + methods = ["*"] + } + "iamcredentials.googleapis.com" = { + methods = ["*"] + } + } + } + }, + { + title = "IR app infra -> seed" + from = { + identities = compact([local.app_infra_cicd_identity]) + sources = { + resources = [ + "projects/${local.cloudbuild_project_number}" + ] + } + } + to = { + resources = [ + "projects/${local.seed_project_number}" + ] + operations = { + "storage.googleapis.com" = { + methods = ["*"] + } + } + } + }, + { + title = "IR app infra -> prjs" + from = { + identities = compact([local.app_infra_cicd_identity]) + sources = { + resources = [ + "projects/${local.cloudbuild_project_number}" + ] + } + } + to = { + resources = local.app_infra_targets_sorted + operations = { + "iam.googleapis.com" = { + methods = ["*"] + } + "compute.googleapis.com" = { + methods = ["*"] + } + } + } + }, + ] + + required_egress_rules = [ + { + title = "ER seed -> cicd" + from = { + identities = [ + "serviceAccount:${local.cloudbuild_project_number}@cloudbuild.gserviceaccount.com" + ] + sources = { + resources = [ + "projects/${local.seed_project_number}" + ] + } + } + to = { + resources = [ + "projects/${local.cloudbuild_project_number}" + ] + operations = { + "cloudbuild.googleapis.com" = { + methods = ["*"] + } + } + } + }, + { + title = "ER cicd -> scc" + from = { + identities = [ + "serviceAccount:${local.organization_service_account}" + ] + sources = { + resources = [ + "projects/${local.cloudbuild_project_number}" + ] + } + } + to = { + resources = [ + "projects/${module.scc_notifications.project_number}" + ] + operations = { + "cloudasset.googleapis.com" = { + methods = ["*"] + } + } + } + }, + ] + + required_egress_rules_app_infra = [ + { + title = "ER app infra -> cicd" + from = { + identities = compact([local.app_infra_pipeline_identity]) + sources = { + resources = local.app_infra_pipeline_source_projects + } + } + to = { + resources = [ + "projects/${local.cloudbuild_project_number}" + ] + operations = { + "cloudbuild.googleapis.com" = { + methods = ["*"] + } + } + } + }, + ] + + required_ingress_rules_list_dry_run = concat( + local.required_ingress_rules_dry_run, + var.required_ingress_rules_app_infra_dry_run ? local.required_ingress_rules_app_infra_dry_run : [], + var.enable_scc_resources_in_terraform ? local.required_ingress_rule_scc_dry_run : [], + var.ingress_policies_dry_run + ) + + required_ingress_rules_list = concat( + local.required_ingress_rules, + var.required_ingress_rules_app_infra ? local.required_ingress_rules_app_infra : [], + var.enable_scc_resources_in_terraform ? local.required_ingress_rule_scc : [], + var.ingress_policies + ) +} + +module "service_control" { + source = "../../modules/service_control" + + access_context_manager_policy_id = var.access_context_manager_policy_id + restricted_services = local.restricted_services + restricted_services_dry_run = local.restricted_services_dry_run + members = distinct(concat([ + "serviceAccount:${local.networks_service_account}", + "serviceAccount:${local.projects_service_account}", + "serviceAccount:${local.organization_service_account}", + "serviceAccount:${local.environment_service_account}", + ], var.perimeter_additional_members)) + members_dry_run = distinct(concat([ + "serviceAccount:${local.networks_service_account}", + "serviceAccount:${local.projects_service_account}", + "serviceAccount:${local.organization_service_account}", + "serviceAccount:${local.environment_service_account}", + ], var.perimeter_additional_members)) + resources = [for k in local.project_keys : local.projects_map[k]] + resource_keys = local.project_keys + resources_dry_run = [for k in local.project_keys_dry_run : local.projects_map_dry_run[k]] + resource_keys_dry_run = local.project_keys_dry_run + ingress_policies_keys_dry_run = local.ingress_policies_keys_dry_run + ingress_policies_keys = local.ingress_policies_keys + egress_policies_keys_dry_run = local.egress_policies_keys_dry_run + egress_policies_keys = local.egress_policies_keys + + ingress_policies_dry_run = local.required_ingress_rules_list_dry_run + ingress_policies = local.required_ingress_rules_list + + egress_policies_dry_run = concat( + local.required_egress_rules_dry_run, + var.required_egress_rules_app_infra_dry_run ? local.required_egress_rules_app_infra_dry_run : [], + var.egress_policies_dry_run + ) + + egress_policies = concat( + local.required_egress_rules, + var.required_egress_rules_app_infra ? local.required_egress_rules_app_infra : [], + var.egress_policies + ) + + depends_on = [ + time_sleep.wait_projects + ] +} diff --git a/1-org/envs/shared/terraform.example.tfvars b/1-org/envs/shared/terraform.example.tfvars index 9ff38bde8..904e5d2bb 100644 --- a/1-org/envs/shared/terraform.example.tfvars +++ b/1-org/envs/shared/terraform.example.tfvars @@ -19,6 +19,11 @@ domains_to_allow = ["example.com"] essential_contacts_domains_to_allow = ["@example.com"] +// Update the following line and add you email in the perimeter_additional_members list. +// You must be in this list to be able to view/access resources in the project protected by the VPC service controls. + +perimeter_additional_members = ["user:YOUR-USER-EMAIL@example.com"] + scc_notification_name = "scc-notify" remote_state_bucket = "REMOTE_STATE_BUCKET" @@ -38,3 +43,17 @@ billing_export_dataset_location = "US" // unique organization-wide it will add a random suffix at each tag key //create_unique_tag_key = true + +access_context_manager_policy_id = ACCESS_CONTEXT_MANAGER_ID + +// If you choose to enable enforced mode, make sure to disable the dry-run variables +// required_ingress_rules_app_infra_dry_run and required_egress_rules_app_infra_dry_run +// before running the 4-projects step + +//required_egress_rules_app_infra_dry_run = true + +//required_egress_rules_app_infra = true + +//required_ingress_rules_app_infra_dry_run = true + +//required_ingress_rules_app_infra = true \ No newline at end of file diff --git a/1-org/envs/shared/variables.tf b/1-org/envs/shared/variables.tf index c68f0c003..9697f9c09 100644 --- a/1-org/envs/shared/variables.tf +++ b/1-org/envs/shared/variables.tf @@ -14,6 +14,12 @@ * limitations under the License. */ +variable "access_context_manager_policy_id" { + description = "The id of the default Access Context Manager policy. Can be obtained by running `gcloud access-context-manager policies list --organization YOUR_ORGANIZATION_ID --format=\"value(name)\"`." + type = string + default = "" +} + variable "enable_hub_and_spoke" { description = "Enable Hub-and-Spoke architecture." type = bool @@ -199,3 +205,194 @@ variable "folder_deletion_protection" { type = string default = true } + +variable "custom_restricted_services" { + description = "List of custom services to be protected by the VPC-SC perimeter. If empty, all supported services (https://cloud.google.com/vpc-service-controls/docs/supported-products) will be protected." + type = list(string) + default = [] +} + +variable "custom_restricted_services_dry_run" { + description = "List of custom services to be protected by the VPC-SC perimeter. If empty, all supported services (https://cloud.google.com/vpc-service-controls/docs/supported-products) will be protected." + type = list(string) + default = [] +} + +variable "ingress_policies_dry_run" { + description = "A list of all [ingress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#ingress-rules-reference) to use in a dry-run perimeter. Each list object has a `from` and `to` value that describes ingress_from and ingress_to.\n\nExample: `[{ from={ sources={ resources=[], access_levels=[] }, identities=[], identity_type=\"ID_TYPE\" }, to={ resources=[], operations={ \"SRV_NAME\"={ OP_TYPE=[] }}}}]`\n\nValid Values:\n`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`\n`SRV_NAME` = \"`*`\" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)\n`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions)" + type = list(object({ + title = optional(string, null) + from = object({ + sources = optional(object({ + resources = optional(list(string), []) + access_levels = optional(list(string), []) + }), {}), + identity_type = optional(string, null) + identities = optional(list(string), null) + }) + to = object({ + operations = optional(map(object({ + methods = optional(list(string), []) + permissions = optional(list(string), []) + })), {}), + roles = optional(list(string), null) + resources = optional(list(string), ["*"]) + }) + })) + default = [] +} + +variable "egress_policies_dry_run" { + description = "A list of all [egress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#egress-rules-reference) to use in a dry-run perimeter. Each list object has a `from` and `to` value that describes egress_from and egress_to.\n\nExample: `[{ from={ identities=[], identity_type=\"ID_TYPE\" }, to={ resources=[], operations={ \"SRV_NAME\"={ OP_TYPE=[] }}}}]`\n\nValid Values:\n`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`\n`SRV_NAME` = \"`*`\" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)\n`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions)" + type = list(object({ + title = optional(string, null) + from = object({ + sources = optional(object({ + resources = optional(list(string), []) + access_levels = optional(list(string), []) + }), {}), + identity_type = optional(string, null) + identities = optional(list(string), null) + }) + to = object({ + operations = optional(map(object({ + methods = optional(list(string), []) + permissions = optional(list(string), []) + })), {}), + roles = optional(list(string), null) + resources = optional(list(string), ["*"]) + external_resources = optional(list(string), []) + }) + })) + default = [] +} + +variable "ingress_policies" { + description = "A list of all [ingress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#ingress-rules-reference) to use in a dry-run perimeter. Each list object has a `from` and `to` value that describes ingress_from and ingress_to.\n\nExample: `[{ from={ sources={ resources=[], access_levels=[] }, identities=[], identity_type=\"ID_TYPE\" }, to={ resources=[], operations={ \"SRV_NAME\"={ OP_TYPE=[] }}}}]`\n\nValid Values:\n`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`\n`SRV_NAME` = \"`*`\" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)\n`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions)" + type = list(object({ + title = optional(string, null) + from = object({ + sources = optional(object({ + resources = optional(list(string), []) + access_levels = optional(list(string), []) + }), {}), + identity_type = optional(string, null) + identities = optional(list(string), null) + }) + to = object({ + operations = optional(map(object({ + methods = optional(list(string), []) + permissions = optional(list(string), []) + })), {}), + roles = optional(list(string), null) + resources = optional(list(string), ["*"]) + }) + })) + default = [] +} + +variable "egress_policies" { + description = "A list of all [egress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#egress-rules-reference) to use in a dry-run perimeter. Each list object has a `from` and `to` value that describes egress_from and egress_to.\n\nExample: `[{ from={ identities=[], identity_type=\"ID_TYPE\" }, to={ resources=[], operations={ \"SRV_NAME\"={ OP_TYPE=[] }}}}]`\n\nValid Values:\n`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`\n`SRV_NAME` = \"`*`\" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)\n`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions)" + type = list(object({ + title = optional(string, null) + from = object({ + sources = optional(object({ + resources = optional(list(string), []) + access_levels = optional(list(string), []) + }), {}), + identity_type = optional(string, null) + identities = optional(list(string), null) + }) + to = object({ + operations = optional(map(object({ + methods = optional(list(string), []) + permissions = optional(list(string), []) + })), {}), + roles = optional(list(string), null) + resources = optional(list(string), ["*"]) + external_resources = optional(list(string), []) + }) + })) + default = [] +} + +variable "perimeter_additional_members" { + description = "The list of additional members to be added to the enforced perimeter access level members list. To be able to see the resources protected by the VPC Service Controls in the perimeter, add your user in this list. Entries must be in the standard GCP form: `user:email@example.com` or `serviceAccount:my-service-account@example.com`." + type = list(string) + default = [] +} + +variable "perimeter_additional_members_dry_run" { + description = "The list of additional members to be added to the dry-run perimeter access level members list. To be able to see the resources protected by the VPC Service Controls in the perimeter, add your user in this list. Entries must be in the standard GCP form: `user:email@example.com` or `serviceAccount:my-service-account@example.com`." + type = list(string) + default = [] +} + +variable "resources" { + description = "A list of GCP resources that are inside of the service perimeter. Currently only projects and VPC networks are allowed." + type = list(string) + default = [] +} + +variable "resources_dry_run" { + description = "A list of GCP resources that are inside of the service perimeter. Currently only projects and VPC networks are allowed. If set, a dry-run policy will be set." + type = list(string) + default = [] +} + +variable "ingress_policies_keys" { + description = "A list of keys to use for the Terraform state. The order should correspond to var.ingress_policies and the keys must not be dynamically computed. If `null`, var.ingress_policies will be used as keys." + type = list(string) + default = [] +} + +variable "egress_policies_keys" { + description = "A list of keys to use for the Terraform state. The order should correspond to var.egress_policies and the keys must not be dynamically computed. If `null`, var.egress_policies will be used as keys." + type = list(string) + default = [] +} + +variable "ingress_policies_keys_dry_run" { + description = "(Dry-run) A list of keys to use for the Terraform state. The order should correspond to var.ingress_policies_dry_run and the keys must not be dynamically computed. If `null`, var.ingress_policies_dry_run will be used as keys." + type = list(string) + default = [] +} + +variable "egress_policies_keys_dry_run" { + description = "(Dry-run) A list of keys to use for the Terraform state. The order should correspond to var.egress_policies_dry_run and the keys must not be dynamically computed. If `null`, var.egress_policies_dry_run will be used as keys." + type = list(string) + default = [] +} + +variable "required_egress_rules_app_infra" { + description = "Required egress rule app infra enforced mode." + type = bool + default = false +} + +variable "required_egress_rules_app_infra_dry_run" { + description = "Required egress rule app infra dry run mode." + type = bool + default = false +} + +variable "required_ingress_rules_app_infra" { + description = "Required ingress rule app infra enforced mode." + type = bool + default = false +} + +variable "required_ingress_rules_app_infra_dry_run" { + description = "Required ingress rule app infra dry run mode." + type = bool + default = false +} + +variable "envs" { + type = map(bool) + default = { + development = true + nonproduction = true + production = true + } +} diff --git a/1-org/modules/cai-monitoring/main.tf b/1-org/modules/cai-monitoring/main.tf index 2286ff260..f089caec5 100644 --- a/1-org/modules/cai-monitoring/main.tf +++ b/1-org/modules/cai-monitoring/main.tf @@ -140,9 +140,12 @@ resource "google_scc_v2_organization_source" "cai_monitoring" { } // Cloud Function +//Using branch 'release-please--branches--main' due to Registry v0.6 incompability with Google Provider. +//TODO: update to the latest Registry version when released. module "cloud_function" { - source = "GoogleCloudPlatform/cloud-functions/google" - version = "~> 0.6" + source = "git::https://github.com/GoogleCloudPlatform/terraform-google-cloud-functions.git?ref=release-please--branches--main" + # source = "GoogleCloudPlatform/cloud-functions/google" + # version = "~> 0.6" function_name = "caiMonitoring" description = "Check on the Organization for members (users, groups and service accounts) that contains the IAM roles listed." diff --git a/1-org/modules/centralized-logging/README.md b/1-org/modules/centralized-logging/README.md index 158a02f13..bd6c0be61 100644 --- a/1-org/modules/centralized-logging/README.md +++ b/1-org/modules/centralized-logging/README.md @@ -74,6 +74,9 @@ module "logging_logbucket" { | Name | Description | |------|-------------| | billing\_sink\_names | Map of log sink names with billing suffix | +| internal\_project\_log\_export | The service account that logging uses to write log entries to the destination. | +| log\_export | The service account that logging uses to write log entries to the destination. | +| log\_export\_billing | The service account that logging uses to write log entries to the destination. | | project\_linked\_dataset\_name | The resource name of the Log Bucket linked BigQuery dataset for the project destination. | | project\_logbucket\_name | The resource name for the Log Bucket created for the project destination. | | pubsub\_destination\_name | The resource name for the destination Pub/Sub. | diff --git a/1-org/modules/centralized-logging/outputs.tf b/1-org/modules/centralized-logging/outputs.tf index 8219994f5..5c9fd6325 100644 --- a/1-org/modules/centralized-logging/outputs.tf +++ b/1-org/modules/centralized-logging/outputs.tf @@ -41,3 +41,24 @@ output "billing_sink_names" { key => "${coalesce(options.logging_sink_name, local.logging_sink_name_map[key])}-billing-${random_string.suffix.result}" } } + +output "log_export_billing" { + description = "The service account that logging uses to write log entries to the destination." + value = { + for k, m in module.log_export_billing : + k => m.writer_identity + } +} + +output "log_export" { + description = "The service account that logging uses to write log entries to the destination." + value = { + for k, m in module.log_export : + k => m.writer_identity + } +} + +output "internal_project_log_export" { + description = "The service account that logging uses to write log entries to the destination." + value = module.internal_project_log_export[0].writer_identity +} diff --git a/3-networks-svpc/modules/shared_vpc/service_control.tf b/1-org/modules/service_control/main.tf similarity index 58% rename from 3-networks-svpc/modules/shared_vpc/service_control.tf rename to 1-org/modules/service_control/main.tf index 16c8905fd..131794244 100644 --- a/3-networks-svpc/modules/shared_vpc/service_control.tf +++ b/1-org/modules/service_control/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * 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. @@ -15,10 +15,10 @@ */ locals { - prefix = "${var.environment_code}_svpc" + prefix = "svpc" access_level_name = "alp_${local.prefix}_members_${random_id.random_access_level_suffix.hex}" access_level_name_dry_run = "alp_${local.prefix}_members_dry_run_${random_id.random_access_level_suffix.hex}" - perimeter_name = "sp_${local.prefix}_default_perimeter_${random_id.random_access_level_suffix.hex}" + perimeter_name = "sp_${local.prefix}_default_common_perimeter_${random_id.random_access_level_suffix.hex}" } resource "random_id" "random_access_level_suffix" { @@ -27,7 +27,7 @@ resource "random_id" "random_access_level_suffix" { module "access_level" { source = "terraform-google-modules/vpc-service-controls/google//modules/access_level" - version = "~> 6.0" + version = "~> 7.1.3" description = "${local.prefix} Access Level for use in an enforced perimeter" policy = var.access_context_manager_policy_id @@ -36,8 +36,10 @@ module "access_level" { } module "access_level_dry_run" { + count = !var.enforce_vpcsc ? 1 : 0 + source = "terraform-google-modules/vpc-service-controls/google//modules/access_level" - version = "~> 6.0" + version = "~> 7.1.3" description = "${local.prefix} Access Level for testing with a dry run perimeter" policy = var.access_context_manager_policy_id @@ -45,56 +47,39 @@ module "access_level_dry_run" { members = var.members_dry_run } -resource "time_sleep" "wait_vpc_sc_propagation" { - create_duration = "60s" - destroy_duration = "60s" - - depends_on = [ - module.main, - google_compute_global_address.private_service_access_address, - google_service_networking_connection.private_vpc_connection, - module.region1_router1, - module.region1_router2, - module.region2_router1, - module.region2_router2, - module.private_service_connect, - google_dns_policy.default_policy, - module.peering_zone, - module.firewall_rules, - google_compute_router.nat_router_region1, - google_compute_address.nat_external_addresses1, - google_compute_router_nat.nat_external_addresses_region1, - google_compute_router.nat_router_region2, - google_compute_address.nat_external_addresses_region2, - google_compute_router_nat.egress_nat_region2, - ] -} - module "regular_service_perimeter" { source = "terraform-google-modules/vpc-service-controls/google//modules/regular_service_perimeter" - version = "~> 6.0" + version = "~> 7.1.3" policy = var.access_context_manager_policy_id perimeter_name = local.perimeter_name description = "Default VPC Service Controls perimeter" # configurations for a perimeter in enforced mode. - resources = var.enforce_vpcsc ? [var.project_number] : [] + resources = var.enforce_vpcsc ? var.resources : [] + resource_keys = var.enforce_vpcsc ? var.resource_keys : [] access_levels = var.enforce_vpcsc ? [module.access_level.name] : [] restricted_services = var.enforce_vpcsc ? var.restricted_services : [] - vpc_accessible_services = var.enforce_vpcsc ? ["RESTRICTED-SERVICES"] : [] + vpc_accessible_services = var.enforce_vpcsc ? ["*"] : [] ingress_policies = var.enforce_vpcsc ? var.ingress_policies : [] + ingress_policies_keys = var.enforce_vpcsc ? var.ingress_policies_keys : [] egress_policies = var.enforce_vpcsc ? var.egress_policies : [] + egress_policies_keys = var.enforce_vpcsc ? var.egress_policies_keys : [] # configurations for a perimeter in dry run mode. - resources_dry_run = [var.project_number] - access_levels_dry_run = [module.access_level_dry_run.name] - restricted_services_dry_run = var.restricted_services_dry_run - vpc_accessible_services_dry_run = ["RESTRICTED-SERVICES"] - ingress_policies_dry_run = var.ingress_policies_dry_run - egress_policies_dry_run = var.egress_policies_dry_run + resources_dry_run = !var.enforce_vpcsc ? var.resources_dry_run : [] + resource_keys_dry_run = !var.enforce_vpcsc ? var.resource_keys_dry_run : [] + access_levels_dry_run = !var.enforce_vpcsc && length(module.access_level_dry_run) > 0 ? [module.access_level_dry_run[0].name] : [] + restricted_services_dry_run = !var.enforce_vpcsc ? var.restricted_services_dry_run : [] + vpc_accessible_services_dry_run = !var.enforce_vpcsc ? ["*"] : [] + ingress_policies_dry_run = !var.enforce_vpcsc ? var.ingress_policies_dry_run : [] + ingress_policies_keys_dry_run = !var.enforce_vpcsc ? var.ingress_policies_keys_dry_run : [] + egress_policies_dry_run = !var.enforce_vpcsc ? var.egress_policies_dry_run : [] + egress_policies_keys_dry_run = !var.enforce_vpcsc ? var.egress_policies_keys_dry_run : [] +} + +resource "time_sleep" "wait_vpc_sc_propagation" { + create_duration = "60s" - depends_on = [ - time_sleep.wait_vpc_sc_propagation - ] + depends_on = [module.regular_service_perimeter] } diff --git a/1-org/modules/service_control/outputs.tf b/1-org/modules/service_control/outputs.tf new file mode 100644 index 000000000..2609dc4ae --- /dev/null +++ b/1-org/modules/service_control/outputs.tf @@ -0,0 +1,35 @@ +/** + * 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. + */ + +output "access_level_name" { + value = local.access_level_name + description = "Access context manager access level name for the enforced perimeter" +} + +output "access_level_name_dry_run" { + value = local.access_level_name_dry_run + description = "Access context manager access level name for the dry-run perimeter" +} + +output "enforce_vpcsc" { + value = var.enforce_vpcsc + description = "Enable the enforced mode for VPC Service Controls. It is not recommended to enable VPC-SC on the first run deploying your foundation. Review [best practices for enabling VPC Service Controls](https://cloud.google.com/vpc-service-controls/docs/enable), then only enforce the perimeter after you have analyzed the access patterns in your dry-run perimeter and created the necessary exceptions for your use cases." +} + +output "service_perimeter_name" { + value = local.perimeter_name + description = "Access context manager service perimeter name for the enforced perimeter" +} diff --git a/1-org/modules/service_control/variables.tf b/1-org/modules/service_control/variables.tf new file mode 100644 index 000000000..53d5f0923 --- /dev/null +++ b/1-org/modules/service_control/variables.tf @@ -0,0 +1,194 @@ +/** + * 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. + */ + +variable "access_context_manager_policy_id" { + type = number + description = "The id of the default Access Context Manager policy. Can be obtained by running `gcloud access-context-manager policies list --organization YOUR_ORGANIZATION_ID --format=\"value(name)\"`." +} + +variable "members" { + type = list(string) + description = "An allowed list of members (users, service accounts) for an access level in an enforced perimeter. The signed-in identity originating the request must be a part of one of the provided members. If not specified, a request may come from any user (logged in/not logged in, etc.). Formats: user:{emailid}, serviceAccount:{emailid}" +} + +variable "members_dry_run" { + type = list(string) + description = "An allowed list of members (users, service accounts) for an access level in a dry run perimeter. The signed-in identity originating the request must be a part of one of the provided members. If not specified, a request may come from any user (logged in/not logged in, etc.). Formats: user:{emailid}, serviceAccount:{emailid}" +} + + +variable "restricted_services" { + type = list(string) + description = "List of services to restrict in an enforced perimeter." +} + +variable "restricted_services_dry_run" { + type = list(string) + description = "List of services to restrict in an enforced perimeter." +} + +variable "enforce_vpcsc" { + description = "Enable the enforced mode for VPC Service Controls. It is not recommended to enable VPC-SC on the first run deploying your foundation. Review [best practices for enabling VPC Service Controls](https://cloud.google.com/vpc-service-controls/docs/enable), then only enforce the perimeter after you have analyzed the access patterns in your dry-run perimeter and created the necessary exceptions for your use cases." + type = bool + default = false +} + +variable "ingress_policies_dry_run" { + description = "A list of all [ingress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#ingress-rules-reference) to use in a dry-run perimeter. Each list object has a `from` and `to` value that describes ingress_from and ingress_to.\n\nExample: `[{ from={ sources={ resources=[], access_levels=[] }, identities=[], identity_type=\"ID_TYPE\" }, to={ resources=[], operations={ \"SRV_NAME\"={ OP_TYPE=[] }}}}]`\n\nValid Values:\n`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`\n`SRV_NAME` = \"`*`\" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)\n`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions)" + type = list(object({ + title = optional(string, null) + from = object({ + sources = optional(object({ + resources = optional(list(string), []) + access_levels = optional(list(string), []) + }), {}), + identity_type = optional(string, null) + identities = optional(list(string), null) + }) + to = object({ + operations = optional(map(object({ + methods = optional(list(string), []) + permissions = optional(list(string), []) + })), {}), + roles = optional(list(string), null) + resources = optional(list(string), ["*"]) + external_resources = optional(list(string), []) + }) + })) + default = [] +} + +variable "egress_policies_dry_run" { + description = "A list of all [egress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#egress-rules-reference) to use in a dry-run perimeter. Each list object has a `from` and `to` value that describes egress_from and egress_to.\n\nExample: `[{ from={ identities=[], identity_type=\"ID_TYPE\" }, to={ resources=[], operations={ \"SRV_NAME\"={ OP_TYPE=[] }}}}]`\n\nValid Values:\n`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`\n`SRV_NAME` = \"`*`\" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)\n`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions)" + type = list(object({ + title = optional(string, null) + from = object({ + sources = optional(object({ + resources = optional(list(string), []) + access_levels = optional(list(string), []) + }), {}), + identity_type = optional(string, null) + identities = optional(list(string), null) + }) + to = object({ + operations = optional(map(object({ + methods = optional(list(string), []) + permissions = optional(list(string), []) + })), {}), + roles = optional(list(string), null) + resources = optional(list(string), ["*"]) + external_resources = optional(list(string), []) + }) + })) + default = [] +} + +variable "ingress_policies" { + description = "A list of all [ingress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#ingress-rules-reference) to use in a dry-run perimeter. Each list object has a `from` and `to` value that describes ingress_from and ingress_to.\n\nExample: `[{ from={ sources={ resources=[], access_levels=[] }, identities=[], identity_type=\"ID_TYPE\" }, to={ resources=[], operations={ \"SRV_NAME\"={ OP_TYPE=[] }}}}]`\n\nValid Values:\n`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`\n`SRV_NAME` = \"`*`\" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)\n`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions)" + type = list(object({ + title = optional(string, null) + from = object({ + sources = optional(object({ + resources = optional(list(string), []) + access_levels = optional(list(string), []) + }), {}), + identity_type = optional(string, null) + identities = optional(list(string), null) + }) + to = object({ + operations = optional(map(object({ + methods = optional(list(string), []) + permissions = optional(list(string), []) + })), {}), + roles = optional(list(string), null) + resources = optional(list(string), ["*"]) + }) + })) + default = [] +} + +variable "egress_policies" { + description = "A list of all [egress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#egress-rules-reference) to use in a dry-run perimeter. Each list object has a `from` and `to` value that describes egress_from and egress_to.\n\nExample: `[{ from={ identities=[], identity_type=\"ID_TYPE\" }, to={ resources=[], operations={ \"SRV_NAME\"={ OP_TYPE=[] }}}}]`\n\nValid Values:\n`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`\n`SRV_NAME` = \"`*`\" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)\n`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions)" + type = list(object({ + title = optional(string, null) + from = object({ + sources = optional(object({ + resources = optional(list(string), []) + access_levels = optional(list(string), []) + }), {}), + identity_type = optional(string, null) + identities = optional(list(string), null) + }) + to = object({ + operations = optional(map(object({ + methods = optional(list(string), []) + permissions = optional(list(string), []) + })), {}), + roles = optional(list(string), null) + resources = optional(list(string), ["*"]) + external_resources = optional(list(string), []) + }) + })) + default = [] +} + +variable "resources" { + description = "A list of GCP resources that are inside of the service perimeter. Currently only projects and VPC networks are allowed." + type = list(string) + default = [] +} + +variable "resource_keys" { + description = "A list of keys to use for the Terraform state. The order should correspond to var.resources and the keys must not be dynamically computed. If `null`, var.resources will be used as keys." + type = list(string) + default = null +} + +variable "resources_dry_run" { + description = "A list of GCP resources that are inside of the service perimeter. Currently only projects and VPC networks are allowed. If set, a dry-run policy will be set." + type = list(string) + default = [] +} + +variable "resource_keys_dry_run" { + description = "A list of keys to use for the Terraform state. The order should correspond to var.resources_dry_run and the keys must not be dynamically computed. If `null`, var.resources_dry_run will be used as keys." + type = list(string) + default = null +} + +variable "ingress_policies_keys" { + description = "A list of keys to use for the Terraform state. The order should correspond to var.ingress_policies and the keys must not be dynamically computed. If `null`, var.ingress_policies will be used as keys." + type = list(string) + default = null +} + +variable "egress_policies_keys" { + description = "A list of keys to use for the Terraform state. The order should correspond to var.egress_policies and the keys must not be dynamically computed. If `null`, var.egress_policies will be used as keys." + type = list(string) + default = null +} + +variable "ingress_policies_keys_dry_run" { + description = "(Dry-run) A list of keys to use for the Terraform state. The order should correspond to var.ingress_policies_dry_run and the keys must not be dynamically computed. If `null`, var.ingress_policies_dry_run will be used as keys." + type = list(string) + default = null +} + +variable "egress_policies_keys_dry_run" { + description = "(Dry-run) A list of keys to use for the Terraform state. The order should correspond to var.egress_policies_dry_run and the keys must not be dynamically computed. If `null`, var.egress_policies_dry_run will be used as keys." + type = list(string) + default = null +} diff --git a/1-org/modules/service_control/versions.tf b/1-org/modules/service_control/versions.tf new file mode 100644 index 000000000..7193ed1bc --- /dev/null +++ b/1-org/modules/service_control/versions.tf @@ -0,0 +1,42 @@ +/** + * 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. + */ + +terraform { + required_version = ">= 0.13" + required_providers { + google = { + // version 6.26.0 removed because of the bug https://github.com/hashicorp/terraform-provider-google/issues/21950 + source = "hashicorp/google" + version = ">= 3.50, != 6.26.0, != 6.27.0, < 7.0" + } + google-beta = { + // version 6.26.0 removed because of the bug https://github.com/hashicorp/terraform-provider-google/issues/21950 + source = "hashicorp/google-beta" + version = ">= 3.50, != 6.26.0, != 6.27.0, < 7.0" + } + random = { + source = "hashicorp/random" + } + } + + provider_meta "google" { + module_name = "blueprints/terraform/terraform-example-foundation:restricted_shared_vpc/v4.1.0" + } + + provider_meta "google-beta" { + module_name = "blueprints/terraform/terraform-example-foundation:restricted_shared_vpc/v4.1.0" + } +} diff --git a/2-environments/README.md b/2-environments/README.md index a75598141..569ba657a 100644 --- a/2-environments/README.md +++ b/2-environments/README.md @@ -210,7 +210,7 @@ See `0-bootstrap` [README-GitHub.md](../0-bootstrap/README-GitHub.md#deploying-s 1. Use `terraform output` to get the backend bucket value from 0-bootstrap output. ```bash - export backend_bucket=$(terraform -chdir="../gcp-bootstrap/" output -raw gcs_bucket_tfstate) + export backend_bucket=$(terraform -chdir="../gcp-bootstrap/envs/shared/" output -raw gcs_bucket_tfstate) echo "remote_state_bucket = ${backend_bucket}" sed -i'' -e "s/REMOTE_STATE_BUCKET/${backend_bucket}/" ./terraform.tfvars @@ -223,10 +223,10 @@ To use the `validate` option of the `tf-wrapper.sh` script, please follow the [i 1. Use `terraform output` to get the Seed project ID and the organization step Terraform service account from gcp-bootstrap output. An environment variable `GOOGLE_IMPERSONATE_SERVICE_ACCOUNT` will be set using the Terraform Service Account to enable impersonation. ```bash - export SEED_PROJECT_ID=$(terraform -chdir="../gcp-bootstrap/" output -raw seed_project_id) + export SEED_PROJECT_ID=$(terraform -chdir="../gcp-bootstrap/envs/shared/" output -raw seed_project_id) echo ${SEED_PROJECT_ID} - export GOOGLE_IMPERSONATE_SERVICE_ACCOUNT=$(terraform -chdir="../gcp-bootstrap/" output -raw environment_step_terraform_service_account_email) + export GOOGLE_IMPERSONATE_SERVICE_ACCOUNT=$(terraform -chdir="../gcp-bootstrap/envs/shared/" output -raw environment_step_terraform_service_account_email) echo ${GOOGLE_IMPERSONATE_SERVICE_ACCOUNT} ``` diff --git a/2-environments/envs/development/README.md b/2-environments/envs/development/README.md index 24b7a7c63..7fbf194e7 100644 --- a/2-environments/envs/development/README.md +++ b/2-environments/envs/development/README.md @@ -16,5 +16,6 @@ | env\_kms\_project\_id | Project for environment Cloud Key Management Service (KMS). | | env\_kms\_project\_number | Project Number for environment Cloud Key Management Service (KMS). | | env\_secrets\_project\_id | Project for environment related secrets. | +| env\_secrets\_project\_number | Project number for environment related secrets. | diff --git a/2-environments/envs/development/outputs.tf b/2-environments/envs/development/outputs.tf index 946657d94..d877d5ae7 100644 --- a/2-environments/envs/development/outputs.tf +++ b/2-environments/envs/development/outputs.tf @@ -24,6 +24,11 @@ output "env_secrets_project_id" { value = module.env.env_secrets_project_id } +output "env_secrets_project_number" { + description = "Project number for environment related secrets." + value = module.env.env_secrets_project_number +} + output "env_kms_project_id" { description = "Project for environment Cloud Key Management Service (KMS)." value = module.env.env_kms_project_id diff --git a/2-environments/envs/nonproduction/README.md b/2-environments/envs/nonproduction/README.md index 24b7a7c63..7fbf194e7 100644 --- a/2-environments/envs/nonproduction/README.md +++ b/2-environments/envs/nonproduction/README.md @@ -16,5 +16,6 @@ | env\_kms\_project\_id | Project for environment Cloud Key Management Service (KMS). | | env\_kms\_project\_number | Project Number for environment Cloud Key Management Service (KMS). | | env\_secrets\_project\_id | Project for environment related secrets. | +| env\_secrets\_project\_number | Project number for environment related secrets. | diff --git a/2-environments/envs/nonproduction/outputs.tf b/2-environments/envs/nonproduction/outputs.tf index 1ca8087d6..9609bf673 100644 --- a/2-environments/envs/nonproduction/outputs.tf +++ b/2-environments/envs/nonproduction/outputs.tf @@ -24,6 +24,11 @@ output "env_secrets_project_id" { value = module.env.env_secrets_project_id } +output "env_secrets_project_number" { + description = "Project number for environment related secrets." + value = module.env.env_secrets_project_number +} + output "env_kms_project_id" { description = "Project for environment Cloud Key Management Service (KMS)." value = module.env.env_kms_project_id diff --git a/2-environments/envs/production/README.md b/2-environments/envs/production/README.md index 629d228db..2d97586d3 100644 --- a/2-environments/envs/production/README.md +++ b/2-environments/envs/production/README.md @@ -18,6 +18,7 @@ | env\_kms\_project\_id | Project for environment Cloud Key Management Service (KMS). | | env\_kms\_project\_number | Project Number for environment Cloud Key Management Service (KMS). | | env\_secrets\_project\_id | Project for environment related secrets. | +| env\_secrets\_project\_number | Project number for environment related secrets. | diff --git a/2-environments/envs/production/outputs.tf b/2-environments/envs/production/outputs.tf index 0d60ebe5f..a5b7956e1 100644 --- a/2-environments/envs/production/outputs.tf +++ b/2-environments/envs/production/outputs.tf @@ -24,6 +24,11 @@ output "env_secrets_project_id" { value = module.env.env_secrets_project_id } +output "env_secrets_project_number" { + description = "Project number for environment related secrets." + value = module.env.env_secrets_project_number +} + output "env_kms_project_id" { description = "Project for environment Cloud Key Management Service (KMS)." value = module.env.env_kms_project_id diff --git a/2-environments/modules/env_baseline/README.md b/2-environments/modules/env_baseline/README.md index 8d9f8ecf1..a0d9b0820 100644 --- a/2-environments/modules/env_baseline/README.md +++ b/2-environments/modules/env_baseline/README.md @@ -22,5 +22,6 @@ | env\_kms\_project\_id | Project for environment Cloud Key Management Service (KMS). | | env\_kms\_project\_number | Project number for envinronment Cloud Key Management Service (KMS). | | env\_secrets\_project\_id | Project for environment secrets. | +| env\_secrets\_project\_number | Project number for environment secrets. | diff --git a/2-environments/modules/env_baseline/kms.tf b/2-environments/modules/env_baseline/kms.tf index 1514991c1..2f7a371ed 100644 --- a/2-environments/modules/env_baseline/kms.tf +++ b/2-environments/modules/env_baseline/kms.tf @@ -35,6 +35,10 @@ module "env_kms" { activate_apis = ["logging.googleapis.com", "cloudkms.googleapis.com", "billingbudgets.googleapis.com"] deletion_policy = var.project_deletion_policy + vpc_service_control_attach_enabled = local.enforce_vpcsc ? "true" : "false" + vpc_service_control_attach_dry_run = !local.enforce_vpcsc ? "true" : "false" + vpc_service_control_perimeter_name = "accessPolicies/${local.access_context_manager_policy_id}/servicePerimeters/${local.perimeter_name}" + labels = { environment = var.env application_name = "env-kms" @@ -45,6 +49,7 @@ module "env_kms" { env_code = var.environment_code vpc = "none" } + budget_alert_pubsub_topic = var.project_budget.kms_alert_pubsub_topic budget_alert_spent_percents = var.project_budget.kms_alert_spent_percents budget_amount = var.project_budget.kms_budget_amount diff --git a/2-environments/modules/env_baseline/outputs.tf b/2-environments/modules/env_baseline/outputs.tf index b949becb9..d7ebff590 100644 --- a/2-environments/modules/env_baseline/outputs.tf +++ b/2-environments/modules/env_baseline/outputs.tf @@ -24,6 +24,11 @@ output "env_secrets_project_id" { value = module.env_secrets.project_id } +output "env_secrets_project_number" { + description = "Project number for environment secrets." + value = module.env_secrets.project_number +} + output "env_kms_project_id" { description = "Project for environment Cloud Key Management Service (KMS)." value = module.env_kms.project_id diff --git a/2-environments/modules/env_baseline/remote.tf b/2-environments/modules/env_baseline/remote.tf index 521bab9cc..539eb90d4 100644 --- a/2-environments/modules/env_baseline/remote.tf +++ b/2-environments/modules/env_baseline/remote.tf @@ -15,13 +15,16 @@ */ locals { - org_id = data.terraform_remote_state.bootstrap.outputs.common_config.org_id - parent = data.terraform_remote_state.bootstrap.outputs.common_config.parent_id - billing_account = data.terraform_remote_state.bootstrap.outputs.common_config.billing_account - project_prefix = data.terraform_remote_state.bootstrap.outputs.common_config.project_prefix - folder_prefix = data.terraform_remote_state.bootstrap.outputs.common_config.folder_prefix - tags = data.terraform_remote_state.org.outputs.tags - required_groups = data.terraform_remote_state.bootstrap.outputs.required_groups + org_id = data.terraform_remote_state.bootstrap.outputs.common_config.org_id + parent = data.terraform_remote_state.bootstrap.outputs.common_config.parent_id + billing_account = data.terraform_remote_state.bootstrap.outputs.common_config.billing_account + project_prefix = data.terraform_remote_state.bootstrap.outputs.common_config.project_prefix + folder_prefix = data.terraform_remote_state.bootstrap.outputs.common_config.folder_prefix + tags = data.terraform_remote_state.org.outputs.tags + required_groups = data.terraform_remote_state.bootstrap.outputs.required_groups + enforce_vpcsc = data.terraform_remote_state.org.outputs.enforce_vpcsc + perimeter_name = data.terraform_remote_state.org.outputs.service_perimeter_name + access_context_manager_policy_id = data.terraform_remote_state.org.outputs.access_context_manager_policy_id } data "terraform_remote_state" "bootstrap" { diff --git a/2-environments/modules/env_baseline/secrets.tf b/2-environments/modules/env_baseline/secrets.tf index 7b04f93ee..2e74a693c 100644 --- a/2-environments/modules/env_baseline/secrets.tf +++ b/2-environments/modules/env_baseline/secrets.tf @@ -35,6 +35,10 @@ module "env_secrets" { activate_apis = ["logging.googleapis.com", "secretmanager.googleapis.com"] deletion_policy = var.project_deletion_policy + vpc_service_control_attach_enabled = local.enforce_vpcsc ? "true" : "false" + vpc_service_control_attach_dry_run = !local.enforce_vpcsc ? "true" : "false" + vpc_service_control_perimeter_name = "accessPolicies/${local.access_context_manager_policy_id}/servicePerimeters/${local.perimeter_name}" + labels = { environment = var.env application_name = "env-secrets" @@ -45,6 +49,7 @@ module "env_secrets" { env_code = var.environment_code vpc = "none" } + budget_alert_pubsub_topic = var.project_budget.secret_alert_pubsub_topic budget_alert_spent_percents = var.project_budget.secret_alert_spent_percents budget_amount = var.project_budget.secret_budget_amount diff --git a/3-networks-hub-and-spoke/README.md b/3-networks-hub-and-spoke/README.md index 362ceb08c..611157b69 100644 --- a/3-networks-hub-and-spoke/README.md +++ b/3-networks-hub-and-spoke/README.md @@ -65,14 +65,6 @@ The purpose of this step is to: 1. 0-bootstrap executed successfully. 1. 1-org executed successfully. 1. 2-environments executed successfully. -1. Obtain the value for the access_context_manager_policy_id variable. It can be obtained by running the following commands. We assume you are at the same level as directory `terraform-example-foundation`, If you run them from another directory, adjust your paths accordingly. - - ```bash - export ORGANIZATION_ID=$(terraform -chdir="terraform-example-foundation/0-bootstrap/" output -json common_config | jq '.org_id' --raw-output) - export ACCESS_CONTEXT_MANAGER_ID=$(gcloud access-context-manager policies list --organization ${ORGANIZATION_ID} --format="value(name)") - echo "access_context_manager_policy_id = ${ACCESS_CONTEXT_MANAGER_ID}" - ``` - 1. For the manual step described in this document, you need to use the same [Terraform](https://www.terraform.io/downloads.html) version used on the build pipeline. Otherwise, you might experience Terraform state snapshot lock errors. @@ -167,44 +159,34 @@ Run `terraform output cloudbuild_project_id` in the `0-bootstrap` folder to get chmod 755 ./tf-wrapper.sh ``` -1. Rename `common.auto.example.tfvars` to `common.auto.tfvars`, rename `shared.auto.example.tfvars` to `shared.auto.tfvars` and rename `access_context.auto.example.tfvars` to `access_context.auto.tfvars`. +2. Rename `common.auto.example.tfvars` to `common.auto.tfvars`, and rename `shared.auto.example.tfvars` to `shared.auto.tfvars`. ```bash mv common.auto.example.tfvars common.auto.tfvars mv shared.auto.example.tfvars shared.auto.tfvars - mv access_context.auto.example.tfvars access_context.auto.tfvars ``` -1. Update `common.auto.tfvars` file with values from your environment and bootstrap. See any of the envs folder [README.md](./envs/production/README.md) files for additional information on the values in the `common.auto.tfvars` file. +3. Update `common.auto.tfvars` file with values from your environment and bootstrap. See any of the envs folder [README.md](./envs/production/README.md) files for additional information on the values in the `common.auto.tfvars` file. Update `shared.auto.tfvars` file with the `target_name_server_addresses`. - Update `access_context.auto.tfvars` file with the `access_context_manager_policy_id`. Use `terraform output` to get the backend bucket value from 0-bootstrap output. ```bash - export ORGANIZATION_ID=$(terraform -chdir="../terraform-example-foundation/0-bootstrap/" output -json common_config | jq '.org_id' --raw-output) - export ACCESS_CONTEXT_MANAGER_ID=$(gcloud access-context-manager policies list --organization ${ORGANIZATION_ID} --format="value(name)") - echo "access_context_manager_policy_id = ${ACCESS_CONTEXT_MANAGER_ID}" - - sed -i'' -e "s/ACCESS_CONTEXT_MANAGER_ID/${ACCESS_CONTEXT_MANAGER_ID}/" ./access_context.auto.tfvars - export backend_bucket=$(terraform -chdir="../terraform-example-foundation/0-bootstrap/" output -raw gcs_bucket_tfstate) echo "remote_state_bucket = ${backend_bucket}" sed -i'' -e "s/REMOTE_STATE_BUCKET/${backend_bucket}/" ./common.auto.tfvars ``` - **Note:** Make sure that you update the `perimeter_additional_members` variable with your user identity in order to be able to view/access resources in the project protected by the VPC Service Controls. - -1. Commit changes +4. Commit changes ```bash git add . git commit -m 'Initialize networks repo' ``` -1. You must manually plan and apply the `shared` environment (only once) since the `development`, `nonproduction` and `production` environments depend on it. -1. To use the `validate` option of the `tf-wrapper.sh` script, please follow the [instructions](https://cloud.google.com/docs/terraform/policy-validation/validate-policies#install) to install the terraform-tools component. -1. Use `terraform output` to get the Cloud Build project ID and the networks step Terraform Service Account from 0-bootstrap output. An environment variable `GOOGLE_IMPERSONATE_SERVICE_ACCOUNT` will be set using the Terraform Service Account to enable impersonation. +5. You must manually plan and apply the `shared` environment (only once) since the `development`, `nonproduction` and `production` environments depend on it. +6. To use the `validate` option of the `tf-wrapper.sh` script, please follow the [instructions](https://cloud.google.com/docs/terraform/policy-validation/validate-policies#install) to install the terraform-tools component. +7. Use `terraform output` to get the Cloud Build project ID and the networks step Terraform Service Account from 0-bootstrap output. An environment variable `GOOGLE_IMPERSONATE_SERVICE_ACCOUNT` will be set using the Terraform Service Account to enable impersonation. ```bash export CLOUD_BUILD_PROJECT_ID=$(terraform -chdir="../terraform-example-foundation/0-bootstrap/" output -raw cloudbuild_project_id) @@ -214,34 +196,34 @@ Run `terraform output cloudbuild_project_id` in the `0-bootstrap` folder to get echo ${GOOGLE_IMPERSONATE_SERVICE_ACCOUNT} ``` -1. Run `init` and `plan` and review output for environment shared. +8. Run `init` and `plan` and review output for environment shared. ```bash ./tf-wrapper.sh init shared ./tf-wrapper.sh plan shared ``` -1. Run `validate` and check for violations. +9. Run `validate` and check for violations. ```bash ./tf-wrapper.sh validate shared $(pwd)/../gcp-policies ${CLOUD_BUILD_PROJECT_ID} ``` -1. Run `apply` shared. +10. Run `apply` shared. ```bash ./tf-wrapper.sh apply shared ``` -1. Push your plan branch to trigger a plan for all environments. Because the - _plan_ branch is not a [named environment branch](../docs/FAQ.md#what-is-a-named-branch)), pushing your _plan_ +11. Push your plan branch to trigger a plan for all environments. Because the + _plan_ branch is not a [named environment branch](../docs/FAQ.md#what-is-a-named-branch), pushing your _plan_ branch triggers _terraform plan_ but not _terraform apply_. Review the plan output in your Cloud Build project https://console.cloud.google.com/cloud-build/builds;region=DEFAULT_REGION?project=YOUR_CLOUD_BUILD_PROJECT_ID ```bash git push --set-upstream origin plan ``` -1. Merge changes to production. Because this is a [named environment branch](../docs/FAQ.md#what-is-a-named-branch), +12. Merge changes to production. Because this is a [named environment branch](../docs/FAQ.md#what-is-a-named-branch), pushing to this branch triggers both _terraform plan_ and _terraform apply_. Review the apply output in your Cloud Build project https://console.cloud.google.com/cloud-build/builds;region=DEFAULT_REGION?project=YOUR_CLOUD_BUILD_PROJECT_ID ```bash @@ -249,8 +231,8 @@ Run `terraform output cloudbuild_project_id` in the `0-bootstrap` folder to get git push origin production ``` -1. After production has been applied, apply development. -1. Merge changes to development. Because this is a [named environment branch](../docs/FAQ.md#what-is-a-named-branch), +13. After production has been applied, apply development. +14. Merge changes to development. Because this is a [named environment branch](../docs/FAQ.md#what-is-a-named-branch), pushing to this branch triggers both _terraform plan_ and _terraform apply_. Review the apply output in your Cloud Build project https://console.cloud.google.com/cloud-build/builds;region=DEFAULT_REGION?project=YOUR_CLOUD_BUILD_PROJECT_ID ```bash @@ -258,8 +240,8 @@ Run `terraform output cloudbuild_project_id` in the `0-bootstrap` folder to get git push origin development ``` -1. After development has been applied, apply nonproduction. -1. Merge changes to nonproduction. Because this is a [named environment branch](../docs/FAQ.md#what-is-a-named-branch), +15. After development has been applied, apply nonproduction. +16. Merge changes to nonproduction. Because this is a [named environment branch](../docs/FAQ.md#what-is-a-named-branch), pushing to this branch triggers both _terraform plan_ and _terraform apply_. Review the apply output in your Cloud Build project https://console.cloud.google.com/cloud-build/builds;region=DEFAULT_REGION?project=YOUR_CLOUD_BUILD_PROJECT_ID ```bash @@ -267,13 +249,13 @@ Run `terraform output cloudbuild_project_id` in the `0-bootstrap` folder to get git push origin nonproduction ``` -1. Before executing the next steps, unset the `GOOGLE_IMPERSONATE_SERVICE_ACCOUNT` environment variable. +17. Before executing the next steps, unset the `GOOGLE_IMPERSONATE_SERVICE_ACCOUNT` environment variable. ```bash unset GOOGLE_IMPERSONATE_SERVICE_ACCOUNT ``` -1. You can now move to the instructions in the [4-projects](../4-projects/README.md) step. +18. You can now move to the instructions in the [4-projects](../4-projects/README.md) step. ### Deploying with Jenkins @@ -306,27 +288,19 @@ See `0-bootstrap` [README-GitHub.md](../0-bootstrap/README-GitHub.md#deploying-s git checkout -b production ``` -1. Rename `common.auto.example.tfvars` to `common.auto.tfvars`, rename `shared.auto.example.tfvars` to `shared.auto.tfvars` and rename `access_context.auto.example.tfvars` to `access_context.auto.tfvars`. +1. Rename `common.auto.example.tfvars` to `common.auto.tfvars`, and rename `shared.auto.example.tfvars` to `shared.auto.tfvars`. ```bash mv common.auto.example.tfvars common.auto.tfvars mv shared.auto.example.tfvars shared.auto.tfvars - mv access_context.auto.example.tfvars access_context.auto.tfvars ``` 1. Update `common.auto.tfvars` file with values from your environment and bootstrap. See any of the envs folder [README.md](./envs/production/README.md) files for additional information on the values in the `common.auto.tfvars` file. 1. Update `shared.auto.tfvars` file with the `target_name_server_addresses`. -1. Update `access_context.auto.tfvars` file with the `access_context_manager_policy_id`. -1. Use `terraform output` to get the backend bucket value from gcp-bootstrap output. +2. Use `terraform output` to get the backend bucket value from gcp-bootstrap output. ```bash - export ORGANIZATION_ID=$(terraform -chdir="../gcp-bootstrap/" output -json common_config | jq '.org_id' --raw-output) - export ACCESS_CONTEXT_MANAGER_ID=$(gcloud access-context-manager policies list --organization ${ORGANIZATION_ID} --format="value(name)") - echo "access_context_manager_policy_id = ${ACCESS_CONTEXT_MANAGER_ID}" - - sed -i'' -e "s/ACCESS_CONTEXT_MANAGER_ID/${ACCESS_CONTEXT_MANAGER_ID}/" ./access_context.auto.tfvars - - export backend_bucket=$(terraform -chdir="../gcp-bootstrap/" output -raw gcs_bucket_tfstate) + export backend_bucket=$(terraform -chdir="../gcp-bootstrap/envs/shared/" output -raw gcs_bucket_tfstate) echo "remote_state_bucket = ${backend_bucket}" sed -i'' -e "s/REMOTE_STATE_BUCKET/${backend_bucket}/" ./common.auto.tfvars @@ -339,10 +313,10 @@ To use the `validate` option of the `tf-wrapper.sh` script, please follow the [i 1. Use `terraform output` to get the Seed project ID and the organization step Terraform service account from 0-bootstrap output. An environment variable `GOOGLE_IMPERSONATE_SERVICE_ACCOUNT` will be set using the Terraform Service Account to enable impersonation. ```bash - export SEED_PROJECT_ID=$(terraform -chdir="../gcp-bootstrap/" output -raw seed_project_id) + export SEED_PROJECT_ID=$(terraform -chdir="../gcp-bootstrap/envs/shared/" output -raw seed_project_id) echo ${SEED_PROJECT_ID} - export GOOGLE_IMPERSONATE_SERVICE_ACCOUNT=$(terraform -chdir="../gcp-bootstrap/" output -raw networks_step_terraform_service_account_email) + export GOOGLE_IMPERSONATE_SERVICE_ACCOUNT=$(terraform -chdir="../gcp-bootstrap/envs/shared/" output -raw networks_step_terraform_service_account_email) echo ${GOOGLE_IMPERSONATE_SERVICE_ACCOUNT} ``` @@ -445,11 +419,3 @@ Before executing the next stages, unset the `GOOGLE_IMPERSONATE_SERVICE_ACCOUNT` ```bash unset GOOGLE_IMPERSONATE_SERVICE_ACCOUNT ``` - -### (Optional) Enforce VPC Service Controls - -Because enabling VPC Service Controls can be a disruptive process, this repo configures VPC Service Controls perimeters in dry run mode by default. This configuration will service traffic that crosses the security perimeter (API requests that originate from inside your perimeter communicating with external resources, or API requests from external resources communicating with resources inside your perimeter) but still allow service traffic normally. - -When you are ready to enforce VPC Service Controls, we recommend that you review the guidance at [Best practices for enabling VPC Service Controls](https://cloud.google.com/vpc-service-controls/docs/enable). After you have added the necessary exceptions and are confident that VPC Service Controls will not disrupt your intended operations, set the variable `enforce_vpcsc` under the module `shared_vpc` to `true` and re-apply this stage. Then re-apply the 4-projects stage, which will inherit the new setting and include those projects inside the enforced perimeter. - -When you need to make changes to an existing enforced perimeter, you can test safely by modifying the configuration of the dry run perimeter. This will log traffic denied by the dry run perimeter without impacting whether the enforced perimeter allows or denies traffic. diff --git a/3-networks-hub-and-spoke/access_context.auto.example.tfvars b/3-networks-hub-and-spoke/access_context.auto.example.tfvars deleted file mode 100644 index 346e3f720..000000000 --- a/3-networks-hub-and-spoke/access_context.auto.example.tfvars +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright 2022 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. - */ - -access_context_manager_policy_id = ACCESS_CONTEXT_MANAGER_ID diff --git a/3-networks-hub-and-spoke/common.auto.example.tfvars b/3-networks-hub-and-spoke/common.auto.example.tfvars index 9ce52ac7c..8181253d1 100644 --- a/3-networks-hub-and-spoke/common.auto.example.tfvars +++ b/3-networks-hub-and-spoke/common.auto.example.tfvars @@ -17,11 +17,6 @@ // The DNS name of peering managed zone. Must end with a period. domain = "example.com." -// Update the following line and add you email in the perimeter_additional_members list. -// You must be in this list to be able to view/access resources in the project protected by the VPC service controls. - -perimeter_additional_members = ["user:YOUR-USER-EMAIL@example.com"] - remote_state_bucket = "REMOTE_STATE_BUCKET" //enable_hub_and_spoke_transitivity = true diff --git a/3-networks-hub-and-spoke/envs/development/README.md b/3-networks-hub-and-spoke/envs/development/README.md index bd29e7c80..f1253478b 100644 --- a/3-networks-hub-and-spoke/envs/development/README.md +++ b/3-networks-hub-and-spoke/envs/development/README.md @@ -15,15 +15,8 @@ The purpose of this step is to set up shared VPCs with default DNS, NAT (optiona | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| access\_context\_manager\_policy\_id | The id of the default Access Context Manager policy created in step `1-org`. Can be obtained by running `gcloud access-context-manager policies list --organization YOUR_ORGANIZATION_ID --format="value(name)"`. | `number` | n/a | yes | | domain | The DNS name of peering managed zone, for instance 'example.com.'. Must end with a period. | `string` | n/a | yes | -| egress\_policies | A list of all [egress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#egress-rules-reference) to use in an enforced perimeter. Each list object has a `from` and `to` value that describes egress\_from and egress\_to.

Example: `[{ from={ identities=[], identity_type="ID_TYPE" }, to={ resources=[], operations={ "SRV_NAME"={ OP_TYPE=[] }}}}]`

Valid Values:
`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`
`SRV_NAME` = "`*`" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)
`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) |
list(object({
from = any
to = any
}))
| `[]` | no | -| egress\_policies\_dry\_run | A list of all [egress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#egress-rules-reference) to use in a dry-run perimeter. Each list object has a `from` and `to` value that describes egress\_from and egress\_to.

Example: `[{ from={ identities=[], identity_type="ID_TYPE" }, to={ resources=[], operations={ "SRV_NAME"={ OP_TYPE=[] }}}}]`

Valid Values:
`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`
`SRV_NAME` = "`*`" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)
`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) |
list(object({
from = any
to = any
}))
| `[]` | no | | enable\_hub\_and\_spoke\_transitivity | Enable transitivity via gateway VMs on Hub-and-Spoke architecture. | `bool` | `false` | no | -| ingress\_policies | A list of all [ingress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#ingress-rules-reference) to use in an enforced perimeter. Each list object has a `from` and `to` value that describes ingress\_from and ingress\_to.

Example: `[{ from={ sources={ resources=[], access_levels=[] }, identities=[], identity_type="ID_TYPE" }, to={ resources=[], operations={ "SRV_NAME"={ OP_TYPE=[] }}}}]`

Valid Values:
`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`
`SRV_NAME` = "`*`" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)
`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) |
list(object({
from = any
to = any
}))
| `[]` | no | -| ingress\_policies\_dry\_run | A list of all [ingress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#ingress-rules-reference) to use in a dry-run perimeter. Each list object has a `from` and `to` value that describes ingress\_from and ingress\_to.

Example: `[{ from={ sources={ resources=[], access_levels=[] }, identities=[], identity_type="ID_TYPE" }, to={ resources=[], operations={ "SRV_NAME"={ OP_TYPE=[] }}}}]`

Valid Values:
`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`
`SRV_NAME` = "`*`" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)
`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) |
list(object({
from = any
to = any
}))
| `[]` | no | -| perimeter\_additional\_members | The list of additional members to be added to the enforced perimeter access level members list. To be able to see the resources protected by the VPC Service Controls in the restricted perimeter, add your user in this list. Entries must be in the standard GCP form: `user:email@example.com` or `serviceAccount:my-service-account@example.com`. | `list(string)` | `[]` | no | -| perimeter\_additional\_members\_dry\_run | The list of additional members to be added to the dry-run perimeter access level members list. To be able to see the resources protected by the VPC Service Controls in the restricted perimeter, add your user in this list. Entries must be in the standard GCP form: `user:email@example.com` or `serviceAccount:my-service-account@example.com`. | `list(string)` | `[]` | no | | remote\_state\_bucket | Backend bucket to load Terraform Remote State Data from previous steps. | `string` | n/a | yes | | tfc\_org\_name | Name of the TFC organization | `string` | `""` | no | @@ -31,13 +24,8 @@ The purpose of this step is to set up shared VPCs with default DNS, NAT (optiona | Name | Description | |------|-------------| -| access\_context\_manager\_policy\_id | Access Context Manager Policy ID. | -| access\_level\_name | Access context manager access level name | -| access\_level\_name\_dry\_run | Access context manager access level name for the dry-run perimeter | -| enforce\_vpcsc | Enable the enforced mode for VPC Service Controls. It is not recommended to enable VPC-SC on the first run deploying your foundation. Review [best practices for enabling VPC Service Controls](https://cloud.google.com/vpc-service-controls/docs/enable), then only enforce the perimeter after you have analyzed the access patterns in your dry-run perimeter and created the necessary exceptions for your use cases. | | network\_name | The name of the VPC being created | | network\_self\_link | The URI of the VPC being created | -| service\_perimeter\_name | Access context manager service perimeter name | | shared\_vpc\_host\_project\_id | The host project ID | | subnets\_ips | The IPs and CIDRs of the subnets being created | | subnets\_names | The names of the subnets being created | diff --git a/3-networks-hub-and-spoke/envs/development/access_context.auto.tfvars b/3-networks-hub-and-spoke/envs/development/access_context.auto.tfvars deleted file mode 120000 index b0cccce77..000000000 --- a/3-networks-hub-and-spoke/envs/development/access_context.auto.tfvars +++ /dev/null @@ -1 +0,0 @@ -../../access_context.auto.tfvars \ No newline at end of file diff --git a/3-networks-hub-and-spoke/envs/development/main.tf b/3-networks-hub-and-spoke/envs/development/main.tf index 1b5b2c35f..081b76d86 100644 --- a/3-networks-hub-and-spoke/envs/development/main.tf +++ b/3-networks-hub-and-spoke/envs/development/main.tf @@ -46,13 +46,9 @@ module "base_env" { env = local.env environment_code = local.environment_code - access_context_manager_policy_id = var.access_context_manager_policy_id - perimeter_additional_members = var.perimeter_additional_members default_region1 = local.default_region1 default_region2 = local.default_region2 domain = var.domain - ingress_policies = var.ingress_policies - egress_policies = var.egress_policies enable_partner_interconnect = false enable_hub_and_spoke_transitivity = var.enable_hub_and_spoke_transitivity private_service_cidr = local.private_service_cidr diff --git a/3-networks-hub-and-spoke/envs/development/outputs.tf b/3-networks-hub-and-spoke/envs/development/outputs.tf index 7dacea96e..adc89e11e 100644 --- a/3-networks-hub-and-spoke/envs/development/outputs.tf +++ b/3-networks-hub-and-spoke/envs/development/outputs.tf @@ -14,11 +14,6 @@ * limitations under the License. */ -output "access_context_manager_policy_id" { - description = "Access Context Manager Policy ID." - value = var.access_context_manager_policy_id -} - output "shared_vpc_host_project_id" { value = module.base_env.shared_vpc_host_project_id description = "The host project ID" @@ -54,22 +49,3 @@ output "subnets_secondary_ranges" { description = "The secondary ranges associated with these subnets" } -output "access_level_name" { - value = module.base_env.access_level_name - description = "Access context manager access level name" -} - -output "access_level_name_dry_run" { - value = module.base_env.access_level_name_dry_run - description = "Access context manager access level name for the dry-run perimeter" -} - -output "enforce_vpcsc" { - value = module.base_env.enforce_vpcsc - description = "Enable the enforced mode for VPC Service Controls. It is not recommended to enable VPC-SC on the first run deploying your foundation. Review [best practices for enabling VPC Service Controls](https://cloud.google.com/vpc-service-controls/docs/enable), then only enforce the perimeter after you have analyzed the access patterns in your dry-run perimeter and created the necessary exceptions for your use cases." -} - -output "service_perimeter_name" { - value = module.base_env.service_perimeter_name - description = "Access context manager service perimeter name" -} diff --git a/3-networks-hub-and-spoke/envs/development/variables.tf b/3-networks-hub-and-spoke/envs/development/variables.tf index cf3061211..c86550386 100644 --- a/3-networks-hub-and-spoke/envs/development/variables.tf +++ b/3-networks-hub-and-spoke/envs/development/variables.tf @@ -19,11 +19,6 @@ variable "remote_state_bucket" { type = string } -variable "access_context_manager_policy_id" { - type = number - description = "The id of the default Access Context Manager policy created in step `1-org`. Can be obtained by running `gcloud access-context-manager policies list --organization YOUR_ORGANIZATION_ID --format=\"value(name)\"`." -} - variable "domain" { type = string description = "The DNS name of peering managed zone, for instance 'example.com.'. Must end with a period." @@ -35,54 +30,6 @@ variable "enable_hub_and_spoke_transitivity" { default = false } -variable "egress_policies" { - description = "A list of all [egress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#egress-rules-reference) to use in an enforced perimeter. Each list object has a `from` and `to` value that describes egress_from and egress_to.\n\nExample: `[{ from={ identities=[], identity_type=\"ID_TYPE\" }, to={ resources=[], operations={ \"SRV_NAME\"={ OP_TYPE=[] }}}}]`\n\nValid Values:\n`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`\n`SRV_NAME` = \"`*`\" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)\n`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions)" - type = list(object({ - from = any - to = any - })) - default = [] -} - -variable "egress_policies_dry_run" { - description = "A list of all [egress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#egress-rules-reference) to use in a dry-run perimeter. Each list object has a `from` and `to` value that describes egress_from and egress_to.\n\nExample: `[{ from={ identities=[], identity_type=\"ID_TYPE\" }, to={ resources=[], operations={ \"SRV_NAME\"={ OP_TYPE=[] }}}}]`\n\nValid Values:\n`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`\n`SRV_NAME` = \"`*`\" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)\n`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions)" - type = list(object({ - from = any - to = any - })) - default = [] -} - -variable "ingress_policies" { - description = "A list of all [ingress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#ingress-rules-reference) to use in an enforced perimeter. Each list object has a `from` and `to` value that describes ingress_from and ingress_to.\n\nExample: `[{ from={ sources={ resources=[], access_levels=[] }, identities=[], identity_type=\"ID_TYPE\" }, to={ resources=[], operations={ \"SRV_NAME\"={ OP_TYPE=[] }}}}]`\n\nValid Values:\n`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`\n`SRV_NAME` = \"`*`\" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)\n`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions)" - type = list(object({ - from = any - to = any - })) - default = [] -} - -variable "ingress_policies_dry_run" { - description = "A list of all [ingress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#ingress-rules-reference) to use in a dry-run perimeter. Each list object has a `from` and `to` value that describes ingress_from and ingress_to.\n\nExample: `[{ from={ sources={ resources=[], access_levels=[] }, identities=[], identity_type=\"ID_TYPE\" }, to={ resources=[], operations={ \"SRV_NAME\"={ OP_TYPE=[] }}}}]`\n\nValid Values:\n`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`\n`SRV_NAME` = \"`*`\" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)\n`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions)" - type = list(object({ - from = any - to = any - })) - default = [] -} - -variable "perimeter_additional_members" { - description = "The list of additional members to be added to the enforced perimeter access level members list. To be able to see the resources protected by the VPC Service Controls in the restricted perimeter, add your user in this list. Entries must be in the standard GCP form: `user:email@example.com` or `serviceAccount:my-service-account@example.com`." - type = list(string) - default = [] -} - -variable "perimeter_additional_members_dry_run" { - description = "The list of additional members to be added to the dry-run perimeter access level members list. To be able to see the resources protected by the VPC Service Controls in the restricted perimeter, add your user in this list. Entries must be in the standard GCP form: `user:email@example.com` or `serviceAccount:my-service-account@example.com`." - type = list(string) - default = [] -} - variable "tfc_org_name" { description = "Name of the TFC organization" type = string diff --git a/3-networks-hub-and-spoke/envs/nonproduction/README.md b/3-networks-hub-and-spoke/envs/nonproduction/README.md index 3447d2f68..f14b1c741 100644 --- a/3-networks-hub-and-spoke/envs/nonproduction/README.md +++ b/3-networks-hub-and-spoke/envs/nonproduction/README.md @@ -15,15 +15,8 @@ The purpose of this step is to set up shared VPCs with default DNS, NAT (optiona | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| access\_context\_manager\_policy\_id | The id of the default Access Context Manager policy created in step `1-org`. Can be obtained by running `gcloud access-context-manager policies list --organization YOUR_ORGANIZATION_ID --format="value(name)"`. | `number` | n/a | yes | | domain | The DNS name of peering managed zone, for instance 'example.com.'. Must end with a period. | `string` | n/a | yes | -| egress\_policies | A list of all [egress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#egress-rules-reference) to use in an enforced perimeter. Each list object has a `from` and `to` value that describes egress\_from and egress\_to.

Example: `[{ from={ identities=[], identity_type="ID_TYPE" }, to={ resources=[], operations={ "SRV_NAME"={ OP_TYPE=[] }}}}]`

Valid Values:
`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`
`SRV_NAME` = "`*`" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)
`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) |
list(object({
from = any
to = any
}))
| `[]` | no | -| egress\_policies\_dry\_run | A list of all [egress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#egress-rules-reference) to use in a dry-run perimeter. Each list object has a `from` and `to` value that describes egress\_from and egress\_to.

Example: `[{ from={ identities=[], identity_type="ID_TYPE" }, to={ resources=[], operations={ "SRV_NAME"={ OP_TYPE=[] }}}}]`

Valid Values:
`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`
`SRV_NAME` = "`*`" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)
`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) |
list(object({
from = any
to = any
}))
| `[]` | no | | enable\_hub\_and\_spoke\_transitivity | Enable transitivity via gateway VMs on Hub-and-Spoke architecture. | `bool` | `false` | no | -| ingress\_policies | A list of all [ingress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#ingress-rules-reference) to use in an enforced perimeter. Each list object has a `from` and `to` value that describes ingress\_from and ingress\_to.

Example: `[{ from={ sources={ resources=[], access_levels=[] }, identities=[], identity_type="ID_TYPE" }, to={ resources=[], operations={ "SRV_NAME"={ OP_TYPE=[] }}}}]`

Valid Values:
`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`
`SRV_NAME` = "`*`" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)
`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) |
list(object({
from = any
to = any
}))
| `[]` | no | -| ingress\_policies\_dry\_run | A list of all [ingress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#ingress-rules-reference) to use in a dry-run perimeter. Each list object has a `from` and `to` value that describes ingress\_from and ingress\_to.

Example: `[{ from={ sources={ resources=[], access_levels=[] }, identities=[], identity_type="ID_TYPE" }, to={ resources=[], operations={ "SRV_NAME"={ OP_TYPE=[] }}}}]`

Valid Values:
`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`
`SRV_NAME` = "`*`" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)
`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) |
list(object({
from = any
to = any
}))
| `[]` | no | -| perimeter\_additional\_members | The list of additional members to be added to the enforced perimeter access level members list. To be able to see the resources protected by the VPC Service Controls in the restricted perimeter, add your user in this list. Entries must be in the standard GCP form: `user:email@example.com` or `serviceAccount:my-service-account@example.com`. | `list(string)` | `[]` | no | -| perimeter\_additional\_members\_dry\_run | The list of additional members to be added to the dry-run perimeter access level members list. To be able to see the resources protected by the VPC Service Controls in the restricted perimeter, add your user in this list. Entries must be in the standard GCP form: `user:email@example.com` or `serviceAccount:my-service-account@example.com`. | `list(string)` | `[]` | no | | remote\_state\_bucket | Backend bucket to load Terraform Remote State Data from previous steps. | `string` | n/a | yes | | tfc\_org\_name | Name of the TFC organization | `string` | `""` | no | @@ -31,13 +24,8 @@ The purpose of this step is to set up shared VPCs with default DNS, NAT (optiona | Name | Description | |------|-------------| -| access\_context\_manager\_policy\_id | Access Context Manager Policy ID. | -| access\_level\_name | Access context manager access level name | -| access\_level\_name\_dry\_run | Access context manager access level name for the dry-run perimeter | -| enforce\_vpcsc | Enable the enforced mode for VPC Service Controls. It is not recommended to enable VPC-SC on the first run deploying your foundation. Review [best practices for enabling VPC Service Controls](https://cloud.google.com/vpc-service-controls/docs/enable), then only enforce the perimeter after you have analyzed the access patterns in your dry-run perimeter and created the necessary exceptions for your use cases. | | network\_name | The name of the VPC being created | | network\_self\_link | The URI of the VPC being created | -| service\_perimeter\_name | Access context manager service perimeter name | | shared\_vpc\_host\_project\_id | The shared vpc host project ID | | subnets\_ips | The IPs and CIDRs of the subnets being created | | subnets\_names | The names of the subnets being created | diff --git a/3-networks-hub-and-spoke/envs/nonproduction/access_context.auto.tfvars b/3-networks-hub-and-spoke/envs/nonproduction/access_context.auto.tfvars deleted file mode 120000 index b0cccce77..000000000 --- a/3-networks-hub-and-spoke/envs/nonproduction/access_context.auto.tfvars +++ /dev/null @@ -1 +0,0 @@ -../../access_context.auto.tfvars \ No newline at end of file diff --git a/3-networks-hub-and-spoke/envs/nonproduction/main.tf b/3-networks-hub-and-spoke/envs/nonproduction/main.tf index 954060efc..2a7287538 100644 --- a/3-networks-hub-and-spoke/envs/nonproduction/main.tf +++ b/3-networks-hub-and-spoke/envs/nonproduction/main.tf @@ -44,25 +44,18 @@ locals { module "base_env" { source = "../../modules/base_env" - env = local.env - environment_code = local.environment_code - access_context_manager_policy_id = var.access_context_manager_policy_id - perimeter_additional_members = var.perimeter_additional_members - perimeter_additional_members_dry_run = var.perimeter_additional_members_dry_run - default_region1 = local.default_region1 - default_region2 = local.default_region2 - domain = var.domain - ingress_policies = var.ingress_policies - ingress_policies_dry_run = var.ingress_policies_dry_run - egress_policies = var.egress_policies - egress_policies_dry_run = var.egress_policies_dry_run - enable_partner_interconnect = false - enable_hub_and_spoke_transitivity = var.enable_hub_and_spoke_transitivity - private_service_cidr = local.private_service_cidr - subnet_primary_ranges = local.subnet_primary_ranges - subnet_proxy_ranges = local.subnet_proxy_ranges - subnet_secondary_ranges = local.subnet_secondary_ranges - private_service_connect_ip = "10.17.0.7" - remote_state_bucket = var.remote_state_bucket - tfc_org_name = var.tfc_org_name + env = local.env + environment_code = local.environment_code + default_region1 = local.default_region1 + default_region2 = local.default_region2 + domain = var.domain + enable_partner_interconnect = false + enable_hub_and_spoke_transitivity = var.enable_hub_and_spoke_transitivity + private_service_cidr = local.private_service_cidr + subnet_primary_ranges = local.subnet_primary_ranges + subnet_proxy_ranges = local.subnet_proxy_ranges + subnet_secondary_ranges = local.subnet_secondary_ranges + private_service_connect_ip = "10.17.0.7" + remote_state_bucket = var.remote_state_bucket + tfc_org_name = var.tfc_org_name } diff --git a/3-networks-hub-and-spoke/envs/nonproduction/outputs.tf b/3-networks-hub-and-spoke/envs/nonproduction/outputs.tf index 700e37837..0a8733624 100644 --- a/3-networks-hub-and-spoke/envs/nonproduction/outputs.tf +++ b/3-networks-hub-and-spoke/envs/nonproduction/outputs.tf @@ -14,11 +14,6 @@ * limitations under the License. */ -output "access_context_manager_policy_id" { - description = "Access Context Manager Policy ID." - value = var.access_context_manager_policy_id -} - output "shared_vpc_host_project_id" { value = module.base_env.shared_vpc_host_project_id description = "The shared vpc host project ID" @@ -53,23 +48,3 @@ output "subnets_secondary_ranges" { value = module.base_env.subnets_secondary_ranges description = "The secondary ranges associated with these subnets" } - -output "access_level_name" { - value = module.base_env.access_level_name - description = "Access context manager access level name" -} - -output "access_level_name_dry_run" { - value = module.base_env.access_level_name_dry_run - description = "Access context manager access level name for the dry-run perimeter" -} - -output "enforce_vpcsc" { - value = module.base_env.enforce_vpcsc - description = "Enable the enforced mode for VPC Service Controls. It is not recommended to enable VPC-SC on the first run deploying your foundation. Review [best practices for enabling VPC Service Controls](https://cloud.google.com/vpc-service-controls/docs/enable), then only enforce the perimeter after you have analyzed the access patterns in your dry-run perimeter and created the necessary exceptions for your use cases." -} - -output "service_perimeter_name" { - value = module.base_env.service_perimeter_name - description = "Access context manager service perimeter name" -} diff --git a/3-networks-hub-and-spoke/envs/nonproduction/variables.tf b/3-networks-hub-and-spoke/envs/nonproduction/variables.tf index cf3061211..c86550386 100644 --- a/3-networks-hub-and-spoke/envs/nonproduction/variables.tf +++ b/3-networks-hub-and-spoke/envs/nonproduction/variables.tf @@ -19,11 +19,6 @@ variable "remote_state_bucket" { type = string } -variable "access_context_manager_policy_id" { - type = number - description = "The id of the default Access Context Manager policy created in step `1-org`. Can be obtained by running `gcloud access-context-manager policies list --organization YOUR_ORGANIZATION_ID --format=\"value(name)\"`." -} - variable "domain" { type = string description = "The DNS name of peering managed zone, for instance 'example.com.'. Must end with a period." @@ -35,54 +30,6 @@ variable "enable_hub_and_spoke_transitivity" { default = false } -variable "egress_policies" { - description = "A list of all [egress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#egress-rules-reference) to use in an enforced perimeter. Each list object has a `from` and `to` value that describes egress_from and egress_to.\n\nExample: `[{ from={ identities=[], identity_type=\"ID_TYPE\" }, to={ resources=[], operations={ \"SRV_NAME\"={ OP_TYPE=[] }}}}]`\n\nValid Values:\n`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`\n`SRV_NAME` = \"`*`\" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)\n`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions)" - type = list(object({ - from = any - to = any - })) - default = [] -} - -variable "egress_policies_dry_run" { - description = "A list of all [egress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#egress-rules-reference) to use in a dry-run perimeter. Each list object has a `from` and `to` value that describes egress_from and egress_to.\n\nExample: `[{ from={ identities=[], identity_type=\"ID_TYPE\" }, to={ resources=[], operations={ \"SRV_NAME\"={ OP_TYPE=[] }}}}]`\n\nValid Values:\n`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`\n`SRV_NAME` = \"`*`\" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)\n`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions)" - type = list(object({ - from = any - to = any - })) - default = [] -} - -variable "ingress_policies" { - description = "A list of all [ingress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#ingress-rules-reference) to use in an enforced perimeter. Each list object has a `from` and `to` value that describes ingress_from and ingress_to.\n\nExample: `[{ from={ sources={ resources=[], access_levels=[] }, identities=[], identity_type=\"ID_TYPE\" }, to={ resources=[], operations={ \"SRV_NAME\"={ OP_TYPE=[] }}}}]`\n\nValid Values:\n`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`\n`SRV_NAME` = \"`*`\" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)\n`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions)" - type = list(object({ - from = any - to = any - })) - default = [] -} - -variable "ingress_policies_dry_run" { - description = "A list of all [ingress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#ingress-rules-reference) to use in a dry-run perimeter. Each list object has a `from` and `to` value that describes ingress_from and ingress_to.\n\nExample: `[{ from={ sources={ resources=[], access_levels=[] }, identities=[], identity_type=\"ID_TYPE\" }, to={ resources=[], operations={ \"SRV_NAME\"={ OP_TYPE=[] }}}}]`\n\nValid Values:\n`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`\n`SRV_NAME` = \"`*`\" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)\n`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions)" - type = list(object({ - from = any - to = any - })) - default = [] -} - -variable "perimeter_additional_members" { - description = "The list of additional members to be added to the enforced perimeter access level members list. To be able to see the resources protected by the VPC Service Controls in the restricted perimeter, add your user in this list. Entries must be in the standard GCP form: `user:email@example.com` or `serviceAccount:my-service-account@example.com`." - type = list(string) - default = [] -} - -variable "perimeter_additional_members_dry_run" { - description = "The list of additional members to be added to the dry-run perimeter access level members list. To be able to see the resources protected by the VPC Service Controls in the restricted perimeter, add your user in this list. Entries must be in the standard GCP form: `user:email@example.com` or `serviceAccount:my-service-account@example.com`." - type = list(string) - default = [] -} - variable "tfc_org_name" { description = "Name of the TFC organization" type = string diff --git a/3-networks-hub-and-spoke/envs/production/README.md b/3-networks-hub-and-spoke/envs/production/README.md index e1e62e277..4652741e7 100644 --- a/3-networks-hub-and-spoke/envs/production/README.md +++ b/3-networks-hub-and-spoke/envs/production/README.md @@ -15,15 +15,8 @@ The purpose of this step is to set up shared VPCs with default DNS, NAT (optiona | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| access\_context\_manager\_policy\_id | The id of the default Access Context Manager policy created in step `1-org`. Can be obtained by running `gcloud access-context-manager policies list --organization YOUR_ORGANIZATION_ID --format="value(name)"`. | `number` | n/a | yes | | domain | The DNS name of peering managed zone, for instance 'example.com.'. Must end with a period. | `string` | n/a | yes | -| egress\_policies | A list of all [egress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#egress-rules-reference) to use in an enforced perimeter. Each list object has a `from` and `to` value that describes egress\_from and egress\_to.

Example: `[{ from={ identities=[], identity_type="ID_TYPE" }, to={ resources=[], operations={ "SRV_NAME"={ OP_TYPE=[] }}}}]`

Valid Values:
`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`
`SRV_NAME` = "`*`" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)
`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) |
list(object({
from = any
to = any
}))
| `[]` | no | -| egress\_policies\_dry\_run | A list of all [egress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#egress-rules-reference) to use in a dry-run perimeter. Each list object has a `from` and `to` value that describes egress\_from and egress\_to.

Example: `[{ from={ identities=[], identity_type="ID_TYPE" }, to={ resources=[], operations={ "SRV_NAME"={ OP_TYPE=[] }}}}]`

Valid Values:
`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`
`SRV_NAME` = "`*`" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)
`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) |
list(object({
from = any
to = any
}))
| `[]` | no | | enable\_hub\_and\_spoke\_transitivity | Enable transitivity via gateway VMs on Hub-and-Spoke architecture. | `bool` | `false` | no | -| ingress\_policies | A list of all [ingress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#ingress-rules-reference) to use in an enforced perimeter. Each list object has a `from` and `to` value that describes ingress\_from and ingress\_to.

Example: `[{ from={ sources={ resources=[], access_levels=[] }, identities=[], identity_type="ID_TYPE" }, to={ resources=[], operations={ "SRV_NAME"={ OP_TYPE=[] }}}}]`

Valid Values:
`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`
`SRV_NAME` = "`*`" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)
`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) |
list(object({
from = any
to = any
}))
| `[]` | no | -| ingress\_policies\_dry\_run | A list of all [ingress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#ingress-rules-reference) to use in a dry-run perimeter. Each list object has a `from` and `to` value that describes ingress\_from and ingress\_to.

Example: `[{ from={ sources={ resources=[], access_levels=[] }, identities=[], identity_type="ID_TYPE" }, to={ resources=[], operations={ "SRV_NAME"={ OP_TYPE=[] }}}}]`

Valid Values:
`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`
`SRV_NAME` = "`*`" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)
`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) |
list(object({
from = any
to = any
}))
| `[]` | no | -| perimeter\_additional\_members | The list of additional members to be added to the enforced perimeter access level members list. To be able to see the resources protected by the VPC Service Controls in the restricted perimeter, add your user in this list. Entries must be in the standard GCP form: `user:email@example.com` or `serviceAccount:my-service-account@example.com`. | `list(string)` | `[]` | no | -| perimeter\_additional\_members\_dry\_run | The list of additional members to be added to the dry-run perimeter access level members list. To be able to see the resources protected by the VPC Service Controls in the restricted perimeter, add your user in this list. Entries must be in the standard GCP form: `user:email@example.com` or `serviceAccount:my-service-account@example.com`. | `list(string)` | `[]` | no | | remote\_state\_bucket | Backend bucket to load Terraform Remote State Data from previous steps. | `string` | n/a | yes | | tfc\_org\_name | Name of the TFC organization | `string` | `""` | no | @@ -31,13 +24,8 @@ The purpose of this step is to set up shared VPCs with default DNS, NAT (optiona | Name | Description | |------|-------------| -| access\_context\_manager\_policy\_id | Access Context Manager Policy ID. | -| access\_level\_name | Access context manager access level name | -| access\_level\_name\_dry\_run | Access context manager access level name for the dry-run perimeter | -| enforce\_vpcsc | Enable the enforced mode for VPC Service Controls. It is not recommended to enable VPC-SC on the first run deploying your foundation. Review [best practices for enabling VPC Service Controls](https://cloud.google.com/vpc-service-controls/docs/enable), then only enforce the perimeter after you have analyzed the access patterns in your dry-run perimeter and created the necessary exceptions for your use cases. | | network\_name | The name of the VPC being created | | network\_self\_link | The URI of the VPC being created | -| service\_perimeter\_name | Access context manager service perimeter name | | shared\_vpc\_host\_project\_id | The shared vpc host project ID | | subnets\_ips | The IPs and CIDRs of the subnets being created | | subnets\_names | The names of the subnets being created | diff --git a/3-networks-hub-and-spoke/envs/production/access_context.auto.tfvars b/3-networks-hub-and-spoke/envs/production/access_context.auto.tfvars deleted file mode 120000 index b0cccce77..000000000 --- a/3-networks-hub-and-spoke/envs/production/access_context.auto.tfvars +++ /dev/null @@ -1 +0,0 @@ -../../access_context.auto.tfvars \ No newline at end of file diff --git a/3-networks-hub-and-spoke/envs/production/main.tf b/3-networks-hub-and-spoke/envs/production/main.tf index 9cf7ffdb3..bfbbf627f 100644 --- a/3-networks-hub-and-spoke/envs/production/main.tf +++ b/3-networks-hub-and-spoke/envs/production/main.tf @@ -44,25 +44,18 @@ locals { module "base_env" { source = "../../modules/base_env" - env = local.env - environment_code = local.environment_code - access_context_manager_policy_id = var.access_context_manager_policy_id - perimeter_additional_members = var.perimeter_additional_members - perimeter_additional_members_dry_run = var.perimeter_additional_members_dry_run - default_region1 = local.default_region1 - default_region2 = local.default_region2 - domain = var.domain - ingress_policies = var.ingress_policies - ingress_policies_dry_run = var.ingress_policies_dry_run - egress_policies = var.egress_policies - egress_policies_dry_run = var.egress_policies_dry_run - enable_partner_interconnect = false - enable_hub_and_spoke_transitivity = var.enable_hub_and_spoke_transitivity - private_service_cidr = local.private_service_cidr - subnet_primary_ranges = local.subnet_primary_ranges - subnet_proxy_ranges = local.subnet_proxy_ranges - subnet_secondary_ranges = local.subnet_secondary_ranges - private_service_connect_ip = "10.17.0.8" - remote_state_bucket = var.remote_state_bucket - tfc_org_name = var.tfc_org_name + env = local.env + environment_code = local.environment_code + default_region1 = local.default_region1 + default_region2 = local.default_region2 + domain = var.domain + enable_partner_interconnect = false + enable_hub_and_spoke_transitivity = var.enable_hub_and_spoke_transitivity + private_service_cidr = local.private_service_cidr + subnet_primary_ranges = local.subnet_primary_ranges + subnet_proxy_ranges = local.subnet_proxy_ranges + subnet_secondary_ranges = local.subnet_secondary_ranges + private_service_connect_ip = "10.17.0.8" + remote_state_bucket = var.remote_state_bucket + tfc_org_name = var.tfc_org_name } diff --git a/3-networks-hub-and-spoke/envs/production/outputs.tf b/3-networks-hub-and-spoke/envs/production/outputs.tf index 700e37837..0a8733624 100644 --- a/3-networks-hub-and-spoke/envs/production/outputs.tf +++ b/3-networks-hub-and-spoke/envs/production/outputs.tf @@ -14,11 +14,6 @@ * limitations under the License. */ -output "access_context_manager_policy_id" { - description = "Access Context Manager Policy ID." - value = var.access_context_manager_policy_id -} - output "shared_vpc_host_project_id" { value = module.base_env.shared_vpc_host_project_id description = "The shared vpc host project ID" @@ -53,23 +48,3 @@ output "subnets_secondary_ranges" { value = module.base_env.subnets_secondary_ranges description = "The secondary ranges associated with these subnets" } - -output "access_level_name" { - value = module.base_env.access_level_name - description = "Access context manager access level name" -} - -output "access_level_name_dry_run" { - value = module.base_env.access_level_name_dry_run - description = "Access context manager access level name for the dry-run perimeter" -} - -output "enforce_vpcsc" { - value = module.base_env.enforce_vpcsc - description = "Enable the enforced mode for VPC Service Controls. It is not recommended to enable VPC-SC on the first run deploying your foundation. Review [best practices for enabling VPC Service Controls](https://cloud.google.com/vpc-service-controls/docs/enable), then only enforce the perimeter after you have analyzed the access patterns in your dry-run perimeter and created the necessary exceptions for your use cases." -} - -output "service_perimeter_name" { - value = module.base_env.service_perimeter_name - description = "Access context manager service perimeter name" -} diff --git a/3-networks-hub-and-spoke/envs/production/variables.tf b/3-networks-hub-and-spoke/envs/production/variables.tf index cf3061211..c86550386 100644 --- a/3-networks-hub-and-spoke/envs/production/variables.tf +++ b/3-networks-hub-and-spoke/envs/production/variables.tf @@ -19,11 +19,6 @@ variable "remote_state_bucket" { type = string } -variable "access_context_manager_policy_id" { - type = number - description = "The id of the default Access Context Manager policy created in step `1-org`. Can be obtained by running `gcloud access-context-manager policies list --organization YOUR_ORGANIZATION_ID --format=\"value(name)\"`." -} - variable "domain" { type = string description = "The DNS name of peering managed zone, for instance 'example.com.'. Must end with a period." @@ -35,54 +30,6 @@ variable "enable_hub_and_spoke_transitivity" { default = false } -variable "egress_policies" { - description = "A list of all [egress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#egress-rules-reference) to use in an enforced perimeter. Each list object has a `from` and `to` value that describes egress_from and egress_to.\n\nExample: `[{ from={ identities=[], identity_type=\"ID_TYPE\" }, to={ resources=[], operations={ \"SRV_NAME\"={ OP_TYPE=[] }}}}]`\n\nValid Values:\n`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`\n`SRV_NAME` = \"`*`\" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)\n`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions)" - type = list(object({ - from = any - to = any - })) - default = [] -} - -variable "egress_policies_dry_run" { - description = "A list of all [egress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#egress-rules-reference) to use in a dry-run perimeter. Each list object has a `from` and `to` value that describes egress_from and egress_to.\n\nExample: `[{ from={ identities=[], identity_type=\"ID_TYPE\" }, to={ resources=[], operations={ \"SRV_NAME\"={ OP_TYPE=[] }}}}]`\n\nValid Values:\n`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`\n`SRV_NAME` = \"`*`\" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)\n`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions)" - type = list(object({ - from = any - to = any - })) - default = [] -} - -variable "ingress_policies" { - description = "A list of all [ingress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#ingress-rules-reference) to use in an enforced perimeter. Each list object has a `from` and `to` value that describes ingress_from and ingress_to.\n\nExample: `[{ from={ sources={ resources=[], access_levels=[] }, identities=[], identity_type=\"ID_TYPE\" }, to={ resources=[], operations={ \"SRV_NAME\"={ OP_TYPE=[] }}}}]`\n\nValid Values:\n`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`\n`SRV_NAME` = \"`*`\" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)\n`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions)" - type = list(object({ - from = any - to = any - })) - default = [] -} - -variable "ingress_policies_dry_run" { - description = "A list of all [ingress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#ingress-rules-reference) to use in a dry-run perimeter. Each list object has a `from` and `to` value that describes ingress_from and ingress_to.\n\nExample: `[{ from={ sources={ resources=[], access_levels=[] }, identities=[], identity_type=\"ID_TYPE\" }, to={ resources=[], operations={ \"SRV_NAME\"={ OP_TYPE=[] }}}}]`\n\nValid Values:\n`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`\n`SRV_NAME` = \"`*`\" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)\n`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions)" - type = list(object({ - from = any - to = any - })) - default = [] -} - -variable "perimeter_additional_members" { - description = "The list of additional members to be added to the enforced perimeter access level members list. To be able to see the resources protected by the VPC Service Controls in the restricted perimeter, add your user in this list. Entries must be in the standard GCP form: `user:email@example.com` or `serviceAccount:my-service-account@example.com`." - type = list(string) - default = [] -} - -variable "perimeter_additional_members_dry_run" { - description = "The list of additional members to be added to the dry-run perimeter access level members list. To be able to see the resources protected by the VPC Service Controls in the restricted perimeter, add your user in this list. Entries must be in the standard GCP form: `user:email@example.com` or `serviceAccount:my-service-account@example.com`." - type = list(string) - default = [] -} - variable "tfc_org_name" { description = "Name of the TFC organization" type = string diff --git a/3-networks-hub-and-spoke/envs/shared/README.md b/3-networks-hub-and-spoke/envs/shared/README.md index 4dce69797..0c47a3896 100644 --- a/3-networks-hub-and-spoke/envs/shared/README.md +++ b/3-networks-hub-and-spoke/envs/shared/README.md @@ -12,13 +12,10 @@ The purpose of this step is to set up the global [DNS Hub](https://cloud.google. | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| access\_context\_manager\_policy\_id | The id of the default Access Context Manager policy created in step `1-org`. Can be obtained by running `gcloud access-context-manager policies list --organization YOUR_ORGANIZATION_ID --format="value(name)"`. | `number` | n/a | yes | | bgp\_asn\_dns | BGP Autonomous System Number (ASN). | `number` | `64667` | no | -| custom\_restricted\_services | List of custom services to be protected by the VPC-SC perimeter. If empty, all supported services (https://cloud.google.com/vpc-service-controls/docs/supported-products) will be protected. | `list(string)` | `[]` | no | | dns\_enable\_logging | Toggle DNS logging for VPC DNS. | `bool` | `true` | no | | dns\_vpc\_flow\_logs | enable\_logging: set to true to enable VPC flow logging for the subnetworks.
aggregation\_interval: Toggles the aggregation interval for collecting flow logs. Increasing the interval time will reduce the amount of generated flow logs for long lasting connections. Possible values are: INTERVAL\_5\_SEC, INTERVAL\_30\_SEC, INTERVAL\_1\_MIN, INTERVAL\_5\_MIN, INTERVAL\_10\_MIN, INTERVAL\_15\_MIN.
flow\_sampling: Set the sampling rate of VPC flow logs within the subnetwork where 1.0 means all collected logs are reported and 0.0 means no logs are reported. The value of the field must be in [0, 1].
metadata: Configures whether metadata fields should be added to the reported VPC flow logs. Possible values are: EXCLUDE\_ALL\_METADATA, INCLUDE\_ALL\_METADATA, CUSTOM\_METADATA.
metadata\_fields: ist of metadata fields that should be added to reported logs. Can only be specified if VPC flow logs for this subnetwork is enabled and "metadata" is set to CUSTOM\_METADATA.
filter\_expr: Export filter used to define which VPC flow logs should be logged, as as CEL expression. See https://cloud.google.com/vpc/docs/flow-logs#filtering for details on how to format this field. |
object({
enable_logging = optional(string, "true")
aggregation_interval = optional(string, "INTERVAL_5_SEC")
flow_sampling = optional(string, "0.5")
metadata = optional(string, "INCLUDE_ALL_METADATA")
metadata_fields = optional(list(string), [])
filter_expr = optional(string, "true")
})
| `{}` | no | | domain | The DNS name of forwarding managed zone, for instance 'example.com'. Must end with a period. | `string` | n/a | yes | -| egress\_policies | A list of all [egress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#egress-rules-reference), each list object has a `from` and `to` value that describes egress\_from and egress\_to.

Example: `[{ from={ identities=[], identity_type="ID_TYPE" }, to={ resources=[], operations={ "SRV_NAME"={ OP_TYPE=[] }}}}]`

Valid Values:
`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`
`SRV_NAME` = "`*`" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)
`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) |
list(object({
from = any
to = any
}))
| `[]` | no | | enable\_dedicated\_interconnect | Enable Dedicated Interconnect in the environment. | `bool` | `false` | no | | enable\_hub\_and\_spoke\_transitivity | Enable transitivity via gateway VMs on Hub-and-Spoke architecture. | `bool` | `false` | no | | enable\_partner\_interconnect | Enable Partner Interconnect in the environment. | `bool` | `false` | no | @@ -31,8 +28,6 @@ The purpose of this step is to set up the global [DNS Hub](https://cloud.google. | hub\_nat\_num\_addresses\_region1 | Number of external IPs to reserve for first Cloud NAT in Shared Hub. | `number` | `2` | no | | hub\_nat\_num\_addresses\_region2 | Number of external IPs to reserve for second Cloud NAT in Shared Hub. | `number` | `2` | no | | hub\_windows\_activation\_enabled | Enable Windows license activation for Windows workloads in Shared Hub. | `bool` | `false` | no | -| ingress\_policies | A list of all [ingress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#ingress-rules-reference), each list object has a `from` and `to` value that describes ingress\_from and ingress\_to.

Example: `[{ from={ sources={ resources=[], access_levels=[] }, identities=[], identity_type="ID_TYPE" }, to={ resources=[], operations={ "SRV_NAME"={ OP_TYPE=[] }}}}]`

Valid Values:
`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`
`SRV_NAME` = "`*`" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)
`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) |
list(object({
from = any
to = any
}))
| `[]` | no | -| perimeter\_additional\_members | The list of additional members to be added to the perimeter access level members list. To be able to see the resources protected by the VPC Service Controls in the Shared perimeter, add your user in this list. Entries must be in the standard GCP form: `user:email@example.com` or `serviceAccount:my-service-account@example.com`. | `list(string)` | n/a | yes | | preactivate\_partner\_interconnect | Preactivate Partner Interconnect VLAN attachment in the environment. | `bool` | `false` | no | | remote\_state\_bucket | Backend bucket to load Terraform Remote State Data from previous steps. | `string` | n/a | yes | | target\_name\_server\_addresses | List of IPv4 address of target name servers for the forwarding zone configuration. See https://cloud.google.com/dns/docs/overview#dns-forwarding-zones for details on target name servers in the context of Cloud DNS forwarding zones. | `list(map(any))` | n/a | yes | diff --git a/3-networks-hub-and-spoke/envs/shared/access_context.auto.tfvars b/3-networks-hub-and-spoke/envs/shared/access_context.auto.tfvars deleted file mode 120000 index b0cccce77..000000000 --- a/3-networks-hub-and-spoke/envs/shared/access_context.auto.tfvars +++ /dev/null @@ -1 +0,0 @@ -../../access_context.auto.tfvars \ No newline at end of file diff --git a/3-networks-hub-and-spoke/envs/shared/main.tf b/3-networks-hub-and-spoke/envs/shared/main.tf index 891fa4eba..be349ba3f 100644 --- a/3-networks-hub-and-spoke/envs/shared/main.tf +++ b/3-networks-hub-and-spoke/envs/shared/main.tf @@ -19,21 +19,4 @@ locals { environment_code = "c" bgp_asn_number = var.enable_partner_interconnect ? "16550" : "64514" dns_bgp_asn_number = var.enable_partner_interconnect ? "16550" : var.bgp_asn_dns - - dedicated_interconnect_egress_policy = var.enable_dedicated_interconnect ? [ - { - "from" = { - "identity_type" = "" - "identities" = ["serviceAccount:${local.networks_service_account}"] - }, - "to" = { - "resources" = ["projects/${local.interconnect_project_number}"] - "operations" = { - "compute.googleapis.com" = { - "methods" = ["*"] - } - } - } - }, - ] : [] } diff --git a/3-networks-hub-and-spoke/envs/shared/net-hubs.tf b/3-networks-hub-and-spoke/envs/shared/net-hubs.tf index bdff242df..f906aa75b 100644 --- a/3-networks-hub-and-spoke/envs/shared/net-hubs.tf +++ b/3-networks-hub-and-spoke/envs/shared/net-hubs.tf @@ -25,132 +25,6 @@ locals { (local.default_region2) = "10.27.0.0/23" } - supported_restricted_service = [ - "accessapproval.googleapis.com", - "adsdatahub.googleapis.com", - "aiplatform.googleapis.com", - "alloydb.googleapis.com", - "analyticshub.googleapis.com", - "apigee.googleapis.com", - "apigeeconnect.googleapis.com", - "artifactregistry.googleapis.com", - "assuredworkloads.googleapis.com", - "automl.googleapis.com", - "baremetalsolution.googleapis.com", - "batch.googleapis.com", - "bigquery.googleapis.com", - "bigquerydatapolicy.googleapis.com", - "bigquerydatatransfer.googleapis.com", - "bigquerymigration.googleapis.com", - "bigqueryreservation.googleapis.com", - "bigtable.googleapis.com", - "binaryauthorization.googleapis.com", - "cloud.googleapis.com", - "cloudasset.googleapis.com", - "cloudbuild.googleapis.com", - "clouddebugger.googleapis.com", - "clouddeploy.googleapis.com", - "clouderrorreporting.googleapis.com", - "cloudfunctions.googleapis.com", - "cloudkms.googleapis.com", - "cloudprofiler.googleapis.com", - "cloudresourcemanager.googleapis.com", - "cloudscheduler.googleapis.com", - "cloudsearch.googleapis.com", - "cloudtrace.googleapis.com", - "composer.googleapis.com", - "compute.googleapis.com", - "connectgateway.googleapis.com", - "contactcenterinsights.googleapis.com", - "container.googleapis.com", - "containeranalysis.googleapis.com", - "containerfilesystem.googleapis.com", - "containerregistry.googleapis.com", - "containerthreatdetection.googleapis.com", - "datacatalog.googleapis.com", - "dataflow.googleapis.com", - "datafusion.googleapis.com", - "datamigration.googleapis.com", - "dataplex.googleapis.com", - "dataproc.googleapis.com", - "datastream.googleapis.com", - "dialogflow.googleapis.com", - "dlp.googleapis.com", - "dns.googleapis.com", - "documentai.googleapis.com", - "domains.googleapis.com", - "eventarc.googleapis.com", - "file.googleapis.com", - "firebaseappcheck.googleapis.com", - "firebaserules.googleapis.com", - "firestore.googleapis.com", - "gameservices.googleapis.com", - "gkebackup.googleapis.com", - "gkeconnect.googleapis.com", - "gkehub.googleapis.com", - "healthcare.googleapis.com", - "iam.googleapis.com", - "iamcredentials.googleapis.com", - "iaptunnel.googleapis.com", - "ids.googleapis.com", - "integrations.googleapis.com", - "kmsinventory.googleapis.com", - "krmapihosting.googleapis.com", - "language.googleapis.com", - "lifesciences.googleapis.com", - "logging.googleapis.com", - "managedidentities.googleapis.com", - "memcache.googleapis.com", - "meshca.googleapis.com", - "meshconfig.googleapis.com", - "metastore.googleapis.com", - "ml.googleapis.com", - "monitoring.googleapis.com", - "networkconnectivity.googleapis.com", - "networkmanagement.googleapis.com", - "networksecurity.googleapis.com", - "networkservices.googleapis.com", - "notebooks.googleapis.com", - "opsconfigmonitoring.googleapis.com", - "orgpolicy.googleapis.com", - "osconfig.googleapis.com", - "oslogin.googleapis.com", - "privateca.googleapis.com", - "pubsub.googleapis.com", - "pubsublite.googleapis.com", - "recaptchaenterprise.googleapis.com", - "recommender.googleapis.com", - "redis.googleapis.com", - "retail.googleapis.com", - "run.googleapis.com", - "secretmanager.googleapis.com", - "servicecontrol.googleapis.com", - "servicedirectory.googleapis.com", - "spanner.googleapis.com", - "speakerid.googleapis.com", - "speech.googleapis.com", - "sqladmin.googleapis.com", - "storage.googleapis.com", - "storagetransfer.googleapis.com", - "sts.googleapis.com", - "texttospeech.googleapis.com", - "timeseriesinsights.googleapis.com", - "tpu.googleapis.com", - "trafficdirector.googleapis.com", - "transcoder.googleapis.com", - "translate.googleapis.com", - "videointelligence.googleapis.com", - "vision.googleapis.com", - "visionai.googleapis.com", - "vmmigration.googleapis.com", - "vpcaccess.googleapis.com", - "webrisk.googleapis.com", - "workflows.googleapis.com", - "workstations.googleapis.com", - ] - - restricted_services = length(var.custom_restricted_services) != 0 ? var.custom_restricted_services : local.supported_restricted_service - restricted_services_dry_run = length(var.custom_restricted_services) != 0 ? var.custom_restricted_services : local.supported_restricted_service } /****************************************** @@ -160,23 +34,10 @@ locals { module "shared_vpc" { source = "../../modules/shared_vpc" - project_id = local.net_hub_project_id - project_number = local.net_hub_project_number - environment_code = local.environment_code - private_service_connect_ip = "10.17.0.5" - access_context_manager_policy_id = var.access_context_manager_policy_id - restricted_services = local.restricted_services - restricted_services_dry_run = local.restricted_services_dry_run - members = distinct(concat([ - "serviceAccount:${local.networks_service_account}", - "serviceAccount:${local.projects_service_account}", - "serviceAccount:${local.organization_service_account}", - ], var.perimeter_additional_members)) - members_dry_run = distinct(concat([ - "serviceAccount:${local.networks_service_account}", - "serviceAccount:${local.projects_service_account}", - "serviceAccount:${local.organization_service_account}", - ], var.perimeter_additional_members)) + project_id = local.net_hub_project_id + project_number = local.net_hub_project_number + environment_code = local.environment_code + private_service_connect_ip = "10.17.0.5" bgp_asn_subnet = local.bgp_asn_number default_region1 = local.default_region1 default_region2 = local.default_region2 @@ -239,12 +100,4 @@ module "shared_vpc" { } ] secondary_ranges = {} - - egress_policies = distinct(concat( - local.dedicated_interconnect_egress_policy, - var.egress_policies - )) - - ingress_policies = var.ingress_policies - } diff --git a/3-networks-hub-and-spoke/envs/shared/variables.tf b/3-networks-hub-and-spoke/envs/shared/variables.tf index 42bcc7530..8ce694951 100644 --- a/3-networks-hub-and-spoke/envs/shared/variables.tf +++ b/3-networks-hub-and-spoke/envs/shared/variables.tf @@ -19,16 +19,6 @@ variable "remote_state_bucket" { type = string } -variable "perimeter_additional_members" { - description = "The list of additional members to be added to the perimeter access level members list. To be able to see the resources protected by the VPC Service Controls in the Shared perimeter, add your user in this list. Entries must be in the standard GCP form: `user:email@example.com` or `serviceAccount:my-service-account@example.com`." - type = list(string) -} - -variable "access_context_manager_policy_id" { - type = number - description = "The id of the default Access Context Manager policy created in step `1-org`. Can be obtained by running `gcloud access-context-manager policies list --organization YOUR_ORGANIZATION_ID --format=\"value(name)\"`." -} - variable "dns_enable_logging" { type = bool description = "Toggle DNS logging for VPC DNS." @@ -169,30 +159,6 @@ variable "enable_hub_and_spoke_transitivity" { default = false } -variable "custom_restricted_services" { - description = "List of custom services to be protected by the VPC-SC perimeter. If empty, all supported services (https://cloud.google.com/vpc-service-controls/docs/supported-products) will be protected." - type = list(string) - default = [] -} - -variable "egress_policies" { - description = "A list of all [egress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#egress-rules-reference), each list object has a `from` and `to` value that describes egress_from and egress_to.\n\nExample: `[{ from={ identities=[], identity_type=\"ID_TYPE\" }, to={ resources=[], operations={ \"SRV_NAME\"={ OP_TYPE=[] }}}}]`\n\nValid Values:\n`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`\n`SRV_NAME` = \"`*`\" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)\n`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions)" - type = list(object({ - from = any - to = any - })) - default = [] -} - -variable "ingress_policies" { - description = "A list of all [ingress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#ingress-rules-reference), each list object has a `from` and `to` value that describes ingress_from and ingress_to.\n\nExample: `[{ from={ sources={ resources=[], access_levels=[] }, identities=[], identity_type=\"ID_TYPE\" }, to={ resources=[], operations={ \"SRV_NAME\"={ OP_TYPE=[] }}}}]`\n\nValid Values:\n`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`\n`SRV_NAME` = \"`*`\" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)\n`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions)" - type = list(object({ - from = any - to = any - })) - default = [] -} - variable "tfc_org_name" { description = "Name of the TFC organization" type = string diff --git a/3-networks-hub-and-spoke/modules/base_env/README.md b/3-networks-hub-and-spoke/modules/base_env/README.md index 908261758..d72e7f773 100644 --- a/3-networks-hub-and-spoke/modules/base_env/README.md +++ b/3-networks-hub-and-spoke/modules/base_env/README.md @@ -3,23 +3,14 @@ | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| access\_context\_manager\_policy\_id | The id of the default Access Context Manager policy created in step `1-org`. Can be obtained by running `gcloud access-context-manager policies list --organization YOUR_ORGANIZATION_ID --format="value(name)"`. | `number` | n/a | yes | -| custom\_restricted\_services | List of custom services to be protected by the enforced VPC-SC perimeter. If empty, all supported services (https://cloud.google.com/vpc-service-controls/docs/supported-products) will be protected. | `list(string)` | `[]` | no | -| custom\_restricted\_services\_dry\_run | List of custom services to be protected by the dry-run VPC-SC perimeter. If empty, all supported services (https://cloud.google.com/vpc-service-controls/docs/supported-products) will be protected. | `list(string)` | `[]` | no | | default\_region1 | First subnet region. The shared vpc modules only configures two regions. | `string` | n/a | yes | | default\_region2 | Second subnet region. The shared vpc modules only configures two regions. | `string` | n/a | yes | | domain | The DNS name of peering managed zone, for instance 'example.com.'. Must end with a period. | `string` | n/a | yes | -| egress\_policies | A list of all [egress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#egress-rules-reference) to use in an enforced perimeter. Each list object has a `from` and `to` value that describes egress\_from and egress\_to.

Example: `[{ from={ identities=[], identity_type="ID_TYPE" }, to={ resources=[], operations={ "SRV_NAME"={ OP_TYPE=[] }}}}]`

Valid Values:
`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`
`SRV_NAME` = "`*`" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)
`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) |
list(object({
from = any
to = any
}))
| `[]` | no | -| egress\_policies\_dry\_run | A list of all [egress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#egress-rules-reference) to use in a dry-run perimeter. Each list object has a `from` and `to` value that describes egress\_from and egress\_to.

Example: `[{ from={ identities=[], identity_type="ID_TYPE" }, to={ resources=[], operations={ "SRV_NAME"={ OP_TYPE=[] }}}}]`

Valid Values:
`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`
`SRV_NAME` = "`*`" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)
`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) |
list(object({
from = any
to = any
}))
| `[]` | no | | enable\_dedicated\_interconnect | Enable Dedicated Interconnect in the environment. | `bool` | `false` | no | | enable\_hub\_and\_spoke\_transitivity | Enable transitivity via gateway VMs on Hub-and-Spoke architecture. | `bool` | `false` | no | | enable\_partner\_interconnect | Enable Partner Interconnect in the environment. | `bool` | `false` | no | | env | The environment to prepare (ex. development) | `string` | n/a | yes | | environment\_code | A short form of the folder level resources (environment) within the Google Cloud organization (ex. d). | `string` | n/a | yes | -| ingress\_policies | A list of all [ingress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#ingress-rules-reference) to use in an enforced perimeter. Each list object has a `from` and `to` value that describes ingress\_from and ingress\_to.

Example: `[{ from={ sources={ resources=[], access_levels=[] }, identities=[], identity_type="ID_TYPE" }, to={ resources=[], operations={ "SRV_NAME"={ OP_TYPE=[] }}}}]`

Valid Values:
`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`
`SRV_NAME` = "`*`" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)
`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) |
list(object({
from = any
to = any
}))
| `[]` | no | -| ingress\_policies\_dry\_run | A list of all [ingress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#ingress-rules-reference) to use in a dry-run perimeter. Each list object has a `from` and `to` value that describes ingress\_from and ingress\_to.

Example: `[{ from={ sources={ resources=[], access_levels=[] }, identities=[], identity_type="ID_TYPE" }, to={ resources=[], operations={ "SRV_NAME"={ OP_TYPE=[] }}}}]`

Valid Values:
`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`
`SRV_NAME` = "`*`" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)
`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) |
list(object({
from = any
to = any
}))
| `[]` | no | -| perimeter\_additional\_members | The list of additional members to be added to the enforced perimeter access level members list. To be able to see the resources protected by the VPC Service Controls in the restricted perimeter, add your user in this list. Entries must be in the standard GCP form: `user:email@example.com` or `serviceAccount:my-service-account@example.com`. | `list(string)` | `[]` | no | -| perimeter\_additional\_members\_dry\_run | The list of additional members to be added to the dry-run perimeter access level members list. To be able to see the resources protected by the VPC Service Controls in the restricted perimeter, add your user in this list. Entries must be in the standard GCP form: `user:email@example.com` or `serviceAccount:my-service-account@example.com`. | `list(string)` | `[]` | no | | private\_service\_cidr | CIDR range for private service networking. Used for Cloud SQL and other managed services in the Shared Vpc. | `string` | n/a | yes | | private\_service\_connect\_ip | The subnet internal IP to be used as the private service connect endpoint in the Shared VPC | `string` | n/a | yes | | remote\_state\_bucket | Backend bucket to load Terraform Remote State Data from previous steps. | `string` | n/a | yes | @@ -34,12 +25,8 @@ | Name | Description | |------|-------------| -| access\_level\_name | Access context manager access level name for the enforced perimeter | -| access\_level\_name\_dry\_run | Access context manager access level name for the dry-run perimeter | -| enforce\_vpcsc | Enable the enforced mode for VPC Service Controls. It is not recommended to enable VPC-SC on the first run deploying your foundation. Review [best practices for enabling VPC Service Controls](https://cloud.google.com/vpc-service-controls/docs/enable), then only enforce the perimeter after you have analyzed the access patterns in your dry-run perimeter and created the necessary exceptions for your use cases. | | network\_name | The name of the VPC being created | | network\_self\_link | The URI of the VPC being created | -| service\_perimeter\_name | Access context manager service perimeter name for the enforced perimeter | | shared\_vpc\_host\_project\_id | The host project ID | | subnets\_ips | The IPs and CIDRs of the subnets being created | | subnets\_names | The names of the subnets being created | diff --git a/3-networks-hub-and-spoke/modules/base_env/main.tf b/3-networks-hub-and-spoke/modules/base_env/main.tf index ab7521696..67ed708db 100644 --- a/3-networks-hub-and-spoke/modules/base_env/main.tf +++ b/3-networks-hub-and-spoke/modules/base_env/main.tf @@ -20,164 +20,22 @@ locals { subnet_aggregates = ["10.8.0.0/18", "10.9.0.0/18", "100.72.0.0/18", "100.73.0.0/18"] hub_subnet_ranges = ["10.8.0.0/24", "10.9.0.0/24"] - - supported_restricted_service = [ - "accessapproval.googleapis.com", - "adsdatahub.googleapis.com", - "aiplatform.googleapis.com", - "alloydb.googleapis.com", - "analyticshub.googleapis.com", - "apigee.googleapis.com", - "apigeeconnect.googleapis.com", - "artifactregistry.googleapis.com", - "assuredworkloads.googleapis.com", - "automl.googleapis.com", - "baremetalsolution.googleapis.com", - "batch.googleapis.com", - "bigquery.googleapis.com", - "bigquerydatapolicy.googleapis.com", - "bigquerydatatransfer.googleapis.com", - "bigquerymigration.googleapis.com", - "bigqueryreservation.googleapis.com", - "bigtable.googleapis.com", - "binaryauthorization.googleapis.com", - "cloud.googleapis.com", - "cloudasset.googleapis.com", - "cloudbuild.googleapis.com", - "clouddebugger.googleapis.com", - "clouddeploy.googleapis.com", - "clouderrorreporting.googleapis.com", - "cloudfunctions.googleapis.com", - "cloudkms.googleapis.com", - "cloudprofiler.googleapis.com", - "cloudresourcemanager.googleapis.com", - "cloudscheduler.googleapis.com", - "cloudsearch.googleapis.com", - "cloudtrace.googleapis.com", - "composer.googleapis.com", - "compute.googleapis.com", - "confidentialcomputing.googleapis.com", - "connectgateway.googleapis.com", - "contactcenterinsights.googleapis.com", - "container.googleapis.com", - "containeranalysis.googleapis.com", - "containerfilesystem.googleapis.com", - "containerregistry.googleapis.com", - "containerthreatdetection.googleapis.com", - "datacatalog.googleapis.com", - "dataflow.googleapis.com", - "datafusion.googleapis.com", - "datamigration.googleapis.com", - "dataplex.googleapis.com", - "dataproc.googleapis.com", - "datastream.googleapis.com", - "dialogflow.googleapis.com", - "dlp.googleapis.com", - "dns.googleapis.com", - "documentai.googleapis.com", - "domains.googleapis.com", - "eventarc.googleapis.com", - "file.googleapis.com", - "firebaseappcheck.googleapis.com", - "firebaserules.googleapis.com", - "firestore.googleapis.com", - "gameservices.googleapis.com", - "gkebackup.googleapis.com", - "gkeconnect.googleapis.com", - "gkehub.googleapis.com", - "healthcare.googleapis.com", - "iam.googleapis.com", - "iamcredentials.googleapis.com", - "iaptunnel.googleapis.com", - "ids.googleapis.com", - "integrations.googleapis.com", - "kmsinventory.googleapis.com", - "krmapihosting.googleapis.com", - "language.googleapis.com", - "lifesciences.googleapis.com", - "logging.googleapis.com", - "managedidentities.googleapis.com", - "memcache.googleapis.com", - "meshca.googleapis.com", - "meshconfig.googleapis.com", - "metastore.googleapis.com", - "ml.googleapis.com", - "monitoring.googleapis.com", - "networkconnectivity.googleapis.com", - "networkmanagement.googleapis.com", - "networksecurity.googleapis.com", - "networkservices.googleapis.com", - "notebooks.googleapis.com", - "opsconfigmonitoring.googleapis.com", - "orgpolicy.googleapis.com", - "osconfig.googleapis.com", - "oslogin.googleapis.com", - "privateca.googleapis.com", - "pubsub.googleapis.com", - "pubsublite.googleapis.com", - "recaptchaenterprise.googleapis.com", - "recommender.googleapis.com", - "redis.googleapis.com", - "retail.googleapis.com", - "run.googleapis.com", - "secretmanager.googleapis.com", - "servicecontrol.googleapis.com", - "servicedirectory.googleapis.com", - "spanner.googleapis.com", - "speakerid.googleapis.com", - "speech.googleapis.com", - "sqladmin.googleapis.com", - "storage.googleapis.com", - "storagetransfer.googleapis.com", - "sts.googleapis.com", - "texttospeech.googleapis.com", - "timeseriesinsights.googleapis.com", - "tpu.googleapis.com", - "trafficdirector.googleapis.com", - "transcoder.googleapis.com", - "translate.googleapis.com", - "videointelligence.googleapis.com", - "vision.googleapis.com", - "visionai.googleapis.com", - "vmmigration.googleapis.com", - "vpcaccess.googleapis.com", - "webrisk.googleapis.com", - "workflows.googleapis.com", - "workstations.googleapis.com", - ] - - restricted_services = length(var.custom_restricted_services) != 0 ? var.custom_restricted_services : local.supported_restricted_service - restricted_services_dry_run = length(var.custom_restricted_services_dry_run) != 0 ? var.custom_restricted_services : local.supported_restricted_service } /****************************************** Shared VPC *****************************************/ + module "shared_vpc" { source = "../shared_vpc" - project_id = local.shared_vpc_project_id - project_number = local.shared_vpc_project_number - net_hub_project_id = local.net_hub_project_id - net_hub_project_number = local.net_hub_project_number - environment_code = var.environment_code - access_context_manager_policy_id = var.access_context_manager_policy_id - restricted_services = local.restricted_services - restricted_services_dry_run = local.restricted_services_dry_run - members = distinct(concat([ - "serviceAccount:${local.networks_service_account}", - "serviceAccount:${local.projects_service_account}", - "serviceAccount:${local.organization_service_account}", - ], var.perimeter_additional_members)) - members_dry_run = distinct(concat([ - "serviceAccount:${local.networks_service_account}", - "serviceAccount:${local.projects_service_account}", - "serviceAccount:${local.organization_service_account}", - ], var.perimeter_additional_members)) + project_id = local.shared_vpc_project_id + project_number = local.shared_vpc_project_number + net_hub_project_id = local.net_hub_project_id + net_hub_project_number = local.net_hub_project_number + environment_code = var.environment_code private_service_cidr = var.private_service_cidr private_service_connect_ip = var.private_service_connect_ip - ingress_policies = var.ingress_policies - egress_policies = var.egress_policies bgp_asn_subnet = local.bgp_asn_number default_region1 = var.default_region1 default_region2 = var.default_region2 diff --git a/3-networks-hub-and-spoke/modules/base_env/outputs.tf b/3-networks-hub-and-spoke/modules/base_env/outputs.tf index e4e7217d9..ec03126b9 100644 --- a/3-networks-hub-and-spoke/modules/base_env/outputs.tf +++ b/3-networks-hub-and-spoke/modules/base_env/outputs.tf @@ -53,23 +53,3 @@ output "subnets_secondary_ranges" { value = module.shared_vpc.subnets_secondary_ranges description = "The secondary ranges associated with these subnets" } - -output "access_level_name" { - value = module.shared_vpc.access_level_name - description = "Access context manager access level name for the enforced perimeter" -} - -output "access_level_name_dry_run" { - value = module.shared_vpc.access_level_name_dry_run - description = "Access context manager access level name for the dry-run perimeter" -} - -output "enforce_vpcsc" { - value = module.shared_vpc.enforce_vpcsc - description = "Enable the enforced mode for VPC Service Controls. It is not recommended to enable VPC-SC on the first run deploying your foundation. Review [best practices for enabling VPC Service Controls](https://cloud.google.com/vpc-service-controls/docs/enable), then only enforce the perimeter after you have analyzed the access patterns in your dry-run perimeter and created the necessary exceptions for your use cases." -} - -output "service_perimeter_name" { - value = module.shared_vpc.service_perimeter_name - description = "Access context manager service perimeter name for the enforced perimeter" -} diff --git a/3-networks-hub-and-spoke/modules/base_env/variables.tf b/3-networks-hub-and-spoke/modules/base_env/variables.tf index 3c9ce405c..cb2d319b9 100644 --- a/3-networks-hub-and-spoke/modules/base_env/variables.tf +++ b/3-networks-hub-and-spoke/modules/base_env/variables.tf @@ -35,11 +35,6 @@ variable "environment_code" { description = "A short form of the folder level resources (environment) within the Google Cloud organization (ex. d)." } -variable "access_context_manager_policy_id" { - type = number - description = "The id of the default Access Context Manager policy created in step `1-org`. Can be obtained by running `gcloud access-context-manager policies list --organization YOUR_ORGANIZATION_ID --format=\"value(name)\"`." -} - variable "default_region1" { type = string description = "First subnet region. The shared vpc modules only configures two regions." @@ -116,67 +111,6 @@ EOT default = {} } -variable "egress_policies" { - description = "A list of all [egress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#egress-rules-reference) to use in an enforced perimeter. Each list object has a `from` and `to` value that describes egress_from and egress_to.\n\nExample: `[{ from={ identities=[], identity_type=\"ID_TYPE\" }, to={ resources=[], operations={ \"SRV_NAME\"={ OP_TYPE=[] }}}}]`\n\nValid Values:\n`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`\n`SRV_NAME` = \"`*`\" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)\n`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions)" - type = list(object({ - from = any - to = any - })) - default = [] -} - -variable "egress_policies_dry_run" { - description = "A list of all [egress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#egress-rules-reference) to use in a dry-run perimeter. Each list object has a `from` and `to` value that describes egress_from and egress_to.\n\nExample: `[{ from={ identities=[], identity_type=\"ID_TYPE\" }, to={ resources=[], operations={ \"SRV_NAME\"={ OP_TYPE=[] }}}}]`\n\nValid Values:\n`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`\n`SRV_NAME` = \"`*`\" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)\n`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions)" - type = list(object({ - from = any - to = any - })) - default = [] -} - -variable "ingress_policies" { - description = "A list of all [ingress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#ingress-rules-reference) to use in an enforced perimeter. Each list object has a `from` and `to` value that describes ingress_from and ingress_to.\n\nExample: `[{ from={ sources={ resources=[], access_levels=[] }, identities=[], identity_type=\"ID_TYPE\" }, to={ resources=[], operations={ \"SRV_NAME\"={ OP_TYPE=[] }}}}]`\n\nValid Values:\n`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`\n`SRV_NAME` = \"`*`\" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)\n`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions)" - type = list(object({ - from = any - to = any - })) - default = [] -} - -variable "ingress_policies_dry_run" { - description = "A list of all [ingress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#ingress-rules-reference) to use in a dry-run perimeter. Each list object has a `from` and `to` value that describes ingress_from and ingress_to.\n\nExample: `[{ from={ sources={ resources=[], access_levels=[] }, identities=[], identity_type=\"ID_TYPE\" }, to={ resources=[], operations={ \"SRV_NAME\"={ OP_TYPE=[] }}}}]`\n\nValid Values:\n`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`\n`SRV_NAME` = \"`*`\" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)\n`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions)" - type = list(object({ - from = any - to = any - })) - default = [] -} - -variable "perimeter_additional_members" { - description = "The list of additional members to be added to the enforced perimeter access level members list. To be able to see the resources protected by the VPC Service Controls in the restricted perimeter, add your user in this list. Entries must be in the standard GCP form: `user:email@example.com` or `serviceAccount:my-service-account@example.com`." - type = list(string) - default = [] -} - -variable "perimeter_additional_members_dry_run" { - description = "The list of additional members to be added to the dry-run perimeter access level members list. To be able to see the resources protected by the VPC Service Controls in the restricted perimeter, add your user in this list. Entries must be in the standard GCP form: `user:email@example.com` or `serviceAccount:my-service-account@example.com`." - type = list(string) - default = [] -} - - -variable "custom_restricted_services" { - description = "List of custom services to be protected by the enforced VPC-SC perimeter. If empty, all supported services (https://cloud.google.com/vpc-service-controls/docs/supported-products) will be protected." - type = list(string) - default = [] -} - -variable "custom_restricted_services_dry_run" { - description = "List of custom services to be protected by the dry-run VPC-SC perimeter. If empty, all supported services (https://cloud.google.com/vpc-service-controls/docs/supported-products) will be protected." - type = list(string) - default = [] -} - variable "tfc_org_name" { description = "Name of the TFC organization" type = string diff --git a/3-networks-hub-and-spoke/modules/shared_vpc/README.md b/3-networks-hub-and-spoke/modules/shared_vpc/README.md index 74c4627d9..43979002e 100644 --- a/3-networks-hub-and-spoke/modules/shared_vpc/README.md +++ b/3-networks-hub-and-spoke/modules/shared_vpc/README.md @@ -3,24 +3,16 @@ | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| access\_context\_manager\_policy\_id | The id of the default Access Context Manager policy. Can be obtained by running `gcloud access-context-manager policies list --organization YOUR_ORGANIZATION_ID --format="value(name)"`. | `number` | n/a | yes | | bgp\_asn\_subnet | BGP ASN for Subnets cloud routers. | `number` | n/a | yes | | default\_region1 | First subnet region. The shared vpc modules only configures two regions. | `string` | n/a | yes | | default\_region2 | Second subnet region. The shared vpc modules only configures two regions. | `string` | n/a | yes | | dns\_enable\_inbound\_forwarding | Toggle inbound query forwarding for VPC DNS. | `bool` | `true` | no | | dns\_enable\_logging | Toggle DNS logging for VPC DNS. | `bool` | `true` | no | | domain | The DNS name of peering managed zone, for instance 'example.com.' | `string` | n/a | yes | -| egress\_policies | A list of all [egress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#egress-rules-reference) to use in an enforced perimeter. Each list object has a `from` and `to` value that describes egress\_from and egress\_to.

Example: `[{ from={ identities=[], identity_type="ID_TYPE" }, to={ resources=[], operations={ "SRV_NAME"={ OP_TYPE=[] }}}}]`

Valid Values:
`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`
`SRV_NAME` = "`*`" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)
`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) |
list(object({
from = any
to = any
}))
| `[]` | no | -| egress\_policies\_dry\_run | A list of all [egress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#egress-rules-reference) to use in a dry-run perimeter. Each list object has a `from` and `to` value that describes egress\_from and egress\_to.

Example: `[{ from={ identities=[], identity_type="ID_TYPE" }, to={ resources=[], operations={ "SRV_NAME"={ OP_TYPE=[] }}}}]`

Valid Values:
`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`
`SRV_NAME` = "`*`" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)
`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) |
list(object({
from = any
to = any
}))
| `[]` | no | | enable\_all\_vpc\_internal\_traffic | Enable firewall policy rule to allow internal traffic (ingress and egress). | `bool` | `false` | no | | enable\_transitivity\_traffic | Enable a firewall policy rule to allow traffic between Hub and Spokes (ingress only). | `bool` | `true` | no | -| enforce\_vpcsc | Enable the enforced mode for VPC Service Controls. It is not recommended to enable VPC-SC on the first run deploying your foundation. Review [best practices for enabling VPC Service Controls](https://cloud.google.com/vpc-service-controls/docs/enable), then only enforce the perimeter after you have analyzed the access patterns in your dry-run perimeter and created the necessary exceptions for your use cases. | `bool` | `false` | no | | environment\_code | A short form of the folder level resources (environment) within the Google Cloud organization. | `string` | n/a | yes | | firewall\_enable\_logging | Toggle firewall logging for VPC Firewalls. | `bool` | `true` | no | -| ingress\_policies | A list of all [ingress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#ingress-rules-reference) to use in an enforced perimeter. Each list object has a `from` and `to` value that describes ingress\_from and ingress\_to.

Example: `[{ from={ sources={ resources=[], access_levels=[] }, identities=[], identity_type="ID_TYPE" }, to={ resources=[], operations={ "SRV_NAME"={ OP_TYPE=[] }}}}]`

Valid Values:
`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`
`SRV_NAME` = "`*`" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)
`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) |
list(object({
from = any
to = any
}))
| `[]` | no | -| ingress\_policies\_dry\_run | A list of all [ingress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#ingress-rules-reference) to use in a dry-run perimeter. Each list object has a `from` and `to` value that describes ingress\_from and ingress\_to.

Example: `[{ from={ sources={ resources=[], access_levels=[] }, identities=[], identity_type="ID_TYPE" }, to={ resources=[], operations={ "SRV_NAME"={ OP_TYPE=[] }}}}]`

Valid Values:
`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`
`SRV_NAME` = "`*`" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)
`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) |
list(object({
from = any
to = any
}))
| `[]` | no | -| members | An allowed list of members (users, service accounts) for an access level in an enforced perimeter. The signed-in identity originating the request must be a part of one of the provided members. If not specified, a request may come from any user (logged in/not logged in, etc.). Formats: user:{emailid}, serviceAccount:{emailid} | `list(string)` | n/a | yes | -| members\_dry\_run | An allowed list of members (users, service accounts) for an access level in a dry run perimeter. The signed-in identity originating the request must be a part of one of the provided members. If not specified, a request may come from any user (logged in/not logged in, etc.). Formats: user:{emailid}, serviceAccount:{emailid} | `list(string)` | n/a | yes | | mode | Network deployment mode, should be set to `hub` or `spoke` when `enable_hub_and_spoke` architecture chosen, keep as `null` otherwise. | `string` | `null` | no | | nat\_bgp\_asn | BGP ASN for NAT cloud routes. If NAT is enabled this variable value must be a value in ranges [64512..65534] or [4200000000..4294967294]. | `number` | `64512` | no | | nat\_enabled | Toggle creation of NAT cloud router. | `bool` | `false` | no | @@ -32,8 +24,6 @@ | private\_service\_connect\_ip | Internal IP to be used as the private service connect endpoint. | `string` | n/a | yes | | project\_id | Project ID for Shared VPC. | `string` | n/a | yes | | project\_number | Project number for Shared VPC. | `number` | n/a | yes | -| restricted\_services | List of services to restrict in an enforced perimeter. | `list(string)` | n/a | yes | -| restricted\_services\_dry\_run | List of services to restrict in a dry-run perimeter. | `list(string)` | n/a | yes | | secondary\_ranges | Secondary ranges that will be used in some of the subnets | `map(list(object({ range_name = string, ip_cidr_range = string })))` | `{}` | no | | subnets | The list of subnets being created |
list(object({
subnet_name = string
subnet_ip = string
subnet_region = string
subnet_private_access = optional(string, "false")
subnet_private_ipv6_access = optional(string)
subnet_flow_logs = optional(string, "false")
subnet_flow_logs_interval = optional(string, "INTERVAL_5_SEC")
subnet_flow_logs_sampling = optional(string, "0.5")
subnet_flow_logs_metadata = optional(string, "INCLUDE_ALL_METADATA")
subnet_flow_logs_filter = optional(string, "true")
subnet_flow_logs_metadata_fields = optional(list(string), [])
description = optional(string)
purpose = optional(string)
role = optional(string)
stack_type = optional(string)
ipv6_access_type = optional(string)
}))
| `[]` | no | | target\_name\_server\_addresses | List of IPv4 address of target name servers for the forwarding zone configuration. See https://cloud.google.com/dns/docs/overview#dns-forwarding-zones for details on target name servers in the context of Cloud DNS forwarding zones. | `list(map(any))` | n/a | yes | @@ -43,10 +33,7 @@ | Name | Description | |------|-------------| -| access\_level\_name | Access context manager access level name for the enforced perimeter | -| access\_level\_name\_dry\_run | Access context manager access level name for the dry-run perimeter | | dns\_policy | The name of the DNS policy being created | -| enforce\_vpcsc | Enable the enforced mode for VPC Service Controls. It is not recommended to enable VPC-SC on the first run deploying your foundation. Review [best practices for enabling VPC Service Controls](https://cloud.google.com/vpc-service-controls/docs/enable), then only enforce the perimeter after you have analyzed the access patterns in your dry-run perimeter and created the necessary exceptions for your use cases. | | firewall\_policy | Policy created for firewall policy rules. | | network\_name | The name of the VPC being created | | network\_self\_link | The URI of the VPC being created | @@ -54,7 +41,6 @@ | region1\_router2 | Router 2 for Region 1 | | region2\_router1 | Router 1 for Region 2 | | region2\_router2 | Router 2 for Region 2 | -| service\_perimeter\_name | Access context manager service perimeter name for the enforced perimeter | | subnets\_ips | The IPs and CIDRs of the subnets being created | | subnets\_names | The names of the subnets being created | | subnets\_regions | The region where the subnets will be created | diff --git a/3-networks-hub-and-spoke/modules/shared_vpc/outputs.tf b/3-networks-hub-and-spoke/modules/shared_vpc/outputs.tf index 617c987fd..7ff41daca 100644 --- a/3-networks-hub-and-spoke/modules/shared_vpc/outputs.tf +++ b/3-networks-hub-and-spoke/modules/shared_vpc/outputs.tf @@ -78,23 +78,3 @@ output "firewall_policy" { value = module.firewall_rules.fw_policy[0].name description = "Policy created for firewall policy rules." } - -output "access_level_name" { - value = local.access_level_name - description = "Access context manager access level name for the enforced perimeter" -} - -output "access_level_name_dry_run" { - value = local.access_level_name_dry_run - description = "Access context manager access level name for the dry-run perimeter" -} - -output "enforce_vpcsc" { - value = var.enforce_vpcsc - description = "Enable the enforced mode for VPC Service Controls. It is not recommended to enable VPC-SC on the first run deploying your foundation. Review [best practices for enabling VPC Service Controls](https://cloud.google.com/vpc-service-controls/docs/enable), then only enforce the perimeter after you have analyzed the access patterns in your dry-run perimeter and created the necessary exceptions for your use cases." -} - -output "service_perimeter_name" { - value = local.perimeter_name - description = "Access context manager service perimeter name for the enforced perimeter" -} diff --git a/3-networks-hub-and-spoke/modules/shared_vpc/service_control.tf b/3-networks-hub-and-spoke/modules/shared_vpc/service_control.tf deleted file mode 100644 index 42921e0f5..000000000 --- a/3-networks-hub-and-spoke/modules/shared_vpc/service_control.tf +++ /dev/null @@ -1,119 +0,0 @@ -/** - * Copyright 2021 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. - */ - -locals { - prefix = "${var.environment_code}_shared_vpc" - access_level_name = "alp_${local.prefix}_members_${random_id.random_access_level_suffix.hex}" - access_level_name_dry_run = "alp_${local.prefix}_members_dry_run_${random_id.random_access_level_suffix.hex}" - perimeter_name = "sp_${local.prefix}_default_perimeter_${random_id.random_access_level_suffix.hex}" - bridge_name = "spb_c_to_${local.prefix}_bridge_${random_id.random_access_level_suffix.hex}" -} - -resource "random_id" "random_access_level_suffix" { - byte_length = 2 -} - -module "access_level" { - source = "terraform-google-modules/vpc-service-controls/google//modules/access_level" - version = "~> 6.0" - - description = "${local.prefix} Access Level" - policy = var.access_context_manager_policy_id - name = local.access_level_name - members = var.members -} - -module "access_level_dry_run" { - source = "terraform-google-modules/vpc-service-controls/google//modules/access_level" - version = "~> 6.0" - - description = "${local.prefix} Access Level for testing with a dry run perimeter" - policy = var.access_context_manager_policy_id - name = local.access_level_name_dry_run - members = var.members_dry_run -} - -resource "time_sleep" "wait_vpc_sc_propagation" { - create_duration = "60s" - destroy_duration = "60s" - - depends_on = [ - module.main, - module.peering, - google_compute_global_address.private_service_access_address, - google_service_networking_connection.private_vpc_connection, - module.region1_router1, - module.region1_router2, - module.region2_router1, - module.region2_router2, - module.private_service_connect, - google_dns_policy.default_policy, - module.peering_zone, - module.firewall_rules, - google_compute_router.nat_router_region1, - google_compute_address.nat_external_addresses1, - google_compute_router_nat.nat_external_addresses_region1, - google_compute_router.nat_router_region2, - google_compute_address.nat_external_addresses_region2, - google_compute_router_nat.egress_nat_region2, - ] -} - -module "regular_service_perimeter" { - source = "terraform-google-modules/vpc-service-controls/google//modules/regular_service_perimeter" - version = "~> 6.0" - - policy = var.access_context_manager_policy_id - perimeter_name = local.perimeter_name - description = "Default VPC Service Controls perimeter" - - # configurations for a perimeter in enforced mode. - resources = var.enforce_vpcsc ? [var.project_number] : [] - access_levels = var.enforce_vpcsc ? [module.access_level.name] : [] - restricted_services = var.enforce_vpcsc ? var.restricted_services : [] - vpc_accessible_services = var.enforce_vpcsc ? ["RESTRICTED-SERVICES"] : [] - ingress_policies = var.enforce_vpcsc ? var.ingress_policies : [] - egress_policies = var.enforce_vpcsc ? var.egress_policies : [] - - # configurations for a perimeter in dry run mode. - resources_dry_run = [var.project_number] - access_levels_dry_run = [module.access_level_dry_run.name] - restricted_services_dry_run = var.restricted_services_dry_run - vpc_accessible_services_dry_run = ["RESTRICTED-SERVICES"] - ingress_policies_dry_run = var.ingress_policies_dry_run - egress_policies_dry_run = var.egress_policies_dry_run - - depends_on = [ - time_sleep.wait_vpc_sc_propagation - ] -} - -resource "google_access_context_manager_service_perimeter" "bridge_to_network_hub_perimeter" { - count = var.mode == "spoke" ? 1 : 0 - - perimeter_type = "PERIMETER_TYPE_BRIDGE" - parent = "accessPolicies/${var.access_context_manager_policy_id}" - name = "accessPolicies/${var.access_context_manager_policy_id}/servicePerimeters/${local.bridge_name}" - title = local.bridge_name - - use_explicit_dry_run_spec = var.enforce_vpcsc ? false : true - - status { - resources = var.enforce_vpcsc ? formatlist("projects/%s", [var.project_number, var.net_hub_project_number]) : [] - } - - depends_on = [module.regular_service_perimeter] -} diff --git a/3-networks-hub-and-spoke/modules/shared_vpc/variables.tf b/3-networks-hub-and-spoke/modules/shared_vpc/variables.tf index b61d41451..9cfc19e96 100644 --- a/3-networks-hub-and-spoke/modules/shared_vpc/variables.tf +++ b/3-networks-hub-and-spoke/modules/shared_vpc/variables.tf @@ -19,11 +19,6 @@ variable "target_name_server_addresses" { type = list(map(any)) } -variable "access_context_manager_policy_id" { - type = number - description = "The id of the default Access Context Manager policy. Can be obtained by running `gcloud access-context-manager policies list --organization YOUR_ORGANIZATION_ID --format=\"value(name)\"`." -} - variable "project_id" { type = string description = "Project ID for Shared VPC." @@ -165,26 +160,6 @@ variable "windows_activation_enabled" { default = false } -variable "members" { - type = list(string) - description = "An allowed list of members (users, service accounts) for an access level in an enforced perimeter. The signed-in identity originating the request must be a part of one of the provided members. If not specified, a request may come from any user (logged in/not logged in, etc.). Formats: user:{emailid}, serviceAccount:{emailid}" -} - -variable "members_dry_run" { - type = list(string) - description = "An allowed list of members (users, service accounts) for an access level in a dry run perimeter. The signed-in identity originating the request must be a part of one of the provided members. If not specified, a request may come from any user (logged in/not logged in, etc.). Formats: user:{emailid}, serviceAccount:{emailid}" -} - -variable "restricted_services" { - type = list(string) - description = "List of services to restrict in an enforced perimeter." -} - -variable "restricted_services_dry_run" { - type = list(string) - description = "List of services to restrict in a dry-run perimeter." -} - variable "enable_all_vpc_internal_traffic" { type = bool description = "Enable firewall policy rule to allow internal traffic (ingress and egress)." @@ -196,45 +171,3 @@ variable "enable_transitivity_traffic" { description = "Enable a firewall policy rule to allow traffic between Hub and Spokes (ingress only)." default = true } - -variable "enforce_vpcsc" { - description = "Enable the enforced mode for VPC Service Controls. It is not recommended to enable VPC-SC on the first run deploying your foundation. Review [best practices for enabling VPC Service Controls](https://cloud.google.com/vpc-service-controls/docs/enable), then only enforce the perimeter after you have analyzed the access patterns in your dry-run perimeter and created the necessary exceptions for your use cases." - type = bool - default = false -} - -variable "egress_policies" { - description = "A list of all [egress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#egress-rules-reference) to use in an enforced perimeter. Each list object has a `from` and `to` value that describes egress_from and egress_to.\n\nExample: `[{ from={ identities=[], identity_type=\"ID_TYPE\" }, to={ resources=[], operations={ \"SRV_NAME\"={ OP_TYPE=[] }}}}]`\n\nValid Values:\n`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`\n`SRV_NAME` = \"`*`\" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)\n`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions)" - type = list(object({ - from = any - to = any - })) - default = [] -} - -variable "egress_policies_dry_run" { - description = "A list of all [egress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#egress-rules-reference) to use in a dry-run perimeter. Each list object has a `from` and `to` value that describes egress_from and egress_to.\n\nExample: `[{ from={ identities=[], identity_type=\"ID_TYPE\" }, to={ resources=[], operations={ \"SRV_NAME\"={ OP_TYPE=[] }}}}]`\n\nValid Values:\n`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`\n`SRV_NAME` = \"`*`\" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)\n`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions)" - type = list(object({ - from = any - to = any - })) - default = [] -} - -variable "ingress_policies" { - description = "A list of all [ingress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#ingress-rules-reference) to use in an enforced perimeter. Each list object has a `from` and `to` value that describes ingress_from and ingress_to.\n\nExample: `[{ from={ sources={ resources=[], access_levels=[] }, identities=[], identity_type=\"ID_TYPE\" }, to={ resources=[], operations={ \"SRV_NAME\"={ OP_TYPE=[] }}}}]`\n\nValid Values:\n`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`\n`SRV_NAME` = \"`*`\" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)\n`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions)" - type = list(object({ - from = any - to = any - })) - default = [] -} - -variable "ingress_policies_dry_run" { - description = "A list of all [ingress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#ingress-rules-reference) to use in a dry-run perimeter. Each list object has a `from` and `to` value that describes ingress_from and ingress_to.\n\nExample: `[{ from={ sources={ resources=[], access_levels=[] }, identities=[], identity_type=\"ID_TYPE\" }, to={ resources=[], operations={ \"SRV_NAME\"={ OP_TYPE=[] }}}}]`\n\nValid Values:\n`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`\n`SRV_NAME` = \"`*`\" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)\n`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions)" - type = list(object({ - from = any - to = any - })) - default = [] -} diff --git a/3-networks-svpc/README.md b/3-networks-svpc/README.md index b8e3a29f2..519f017ed 100644 --- a/3-networks-svpc/README.md +++ b/3-networks-svpc/README.md @@ -65,14 +65,6 @@ The purpose of this step is to: 1. 0-bootstrap executed successfully. 1. 1-org executed successfully. 1. 2-environments executed successfully. -1. Obtain the value for the access_context_manager_policy_id variable. It can be obtained by running the following commands. We assume you are at the same level as directory `terraform-example-foundation`, If you run them from another directory, adjust your paths accordingly. - - ```bash - export ORGANIZATION_ID=$(terraform -chdir="terraform-example-foundation/0-bootstrap/" output -json common_config | jq '.org_id' --raw-output) - export ACCESS_CONTEXT_MANAGER_ID=$(gcloud access-context-manager policies list --organization ${ORGANIZATION_ID} --format="value(name)") - echo "access_context_manager_policy_id = ${ACCESS_CONTEXT_MANAGER_ID}" - ``` - 1. For the manual step described in this document, you need to use the same [Terraform](https://www.terraform.io/downloads.html) version used on the build pipeline. Otherwise, you might experience Terraform state snapshot lock errors. @@ -163,44 +155,34 @@ Run `terraform output cloudbuild_project_id` in the `0-bootstrap` folder to get chmod 755 ./tf-wrapper.sh ``` -1. Rename `common.auto.example.tfvars` to `common.auto.tfvars`, rename `production.auto.example.tfvars` to `production.auto.tfvars` and rename `access_context.auto.example.tfvars` to `access_context.auto.tfvars`. +2. Rename `common.auto.example.tfvars` to `common.auto.tfvars`, rename `production.auto.example.tfvars` to `production.auto.tfvars`. ```bash mv common.auto.example.tfvars common.auto.tfvars mv production.auto.example.tfvars production.auto.tfvars - mv access_context.auto.example.tfvars access_context.auto.tfvars ``` -1. Update `common.auto.tfvars` file with values from your environment and bootstrap. See any of the envs folder [README.md](./envs/production/README.md) files for additional information on the values in the `common.auto.tfvars` file. +3. Update `common.auto.tfvars` file with values from your environment and bootstrap. See any of the envs folder [README.md](./envs/production/README.md) files for additional information on the values in the `common.auto.tfvars` file. Update `production.auto.tfvars` file with the `target_name_server_addresses`. - Update `access_context.auto.tfvars` file with the `access_context_manager_policy_id`. Use `terraform output` to get the backend bucket value from 0-bootstrap output. ```bash - export ORGANIZATION_ID=$(terraform -chdir="../terraform-example-foundation/0-bootstrap/" output -json common_config | jq '.org_id' --raw-output) - export ACCESS_CONTEXT_MANAGER_ID=$(gcloud access-context-manager policies list --organization ${ORGANIZATION_ID} --format="value(name)") - echo "access_context_manager_policy_id = ${ACCESS_CONTEXT_MANAGER_ID}" - - sed -i'' -e "s/ACCESS_CONTEXT_MANAGER_ID/${ACCESS_CONTEXT_MANAGER_ID}/" ./access_context.auto.tfvars - export backend_bucket=$(terraform -chdir="../terraform-example-foundation/0-bootstrap/" output -raw gcs_bucket_tfstate) echo "remote_state_bucket = ${backend_bucket}" sed -i'' -e "s/REMOTE_STATE_BUCKET/${backend_bucket}/" ./common.auto.tfvars ``` - **Note:** Make sure that you update the `perimeter_additional_members` variable with your user identity in order to be able to view/access resources in the project protected by the VPC Service Controls. - -1. Commit changes +4. Commit changes ```bash git add . git commit -m 'Initialize networks repo' ``` -1. You must manually plan and apply the `shared` environment (only once) since the `development`, `nonproduction` and `production` environments depend on it. -1. To use the `validate` option of the `tf-wrapper.sh` script, please follow the [instructions](https://cloud.google.com/docs/terraform/policy-validation/validate-policies#install) to install the terraform-tools component. -1. Use `terraform output` to get the Cloud Build project ID and the networks step Terraform Service Account from 0-bootstrap output. An environment variable `GOOGLE_IMPERSONATE_SERVICE_ACCOUNT` will be set using the Terraform Service Account to enable impersonation. +5. You must manually plan and apply the `shared` environment (only once) since the `development`, `nonproduction` and `production` environments depend on it. +6. To use the `validate` option of the `tf-wrapper.sh` script, please follow the [instructions](https://cloud.google.com/docs/terraform/policy-validation/validate-policies#install) to install the terraform-tools component. +7. Use `terraform output` to get the Cloud Build project ID and the networks step Terraform Service Account from 0-bootstrap output. An environment variable `GOOGLE_IMPERSONATE_SERVICE_ACCOUNT` will be set using the Terraform Service Account to enable impersonation. ```bash export CLOUD_BUILD_PROJECT_ID=$(terraform -chdir="../terraform-example-foundation/0-bootstrap/" output -raw cloudbuild_project_id) @@ -210,48 +192,48 @@ Run `terraform output cloudbuild_project_id` in the `0-bootstrap` folder to get echo ${GOOGLE_IMPERSONATE_SERVICE_ACCOUNT} ``` -1. Run `init` and `plan` and review output for environment shared. +8. Run `init` and `plan` and review output for environment shared. ```bash ./tf-wrapper.sh init shared ./tf-wrapper.sh plan shared ``` -1. Run `validate` and check for violations. +9. Run `validate` and check for violations. ```bash ./tf-wrapper.sh validate shared $(pwd)/../gcp-policies ${CLOUD_BUILD_PROJECT_ID} ``` -1. Run `apply` shared. +10. Run `apply` shared. ```bash ./tf-wrapper.sh apply shared ``` -1. You must manually plan and apply the `production` environment since the `development`, `nonproduction` and `plan` environments depend on it. +11. You must manually plan and apply the `production` environment since the `development`, `nonproduction` and `plan` environments depend on it. ```bash git checkout -b production ``` -1. Run `init` and `plan` and review output for environment production. +12. Run `init` and `plan` and review output for environment production. ```bash ./tf-wrapper.sh init production ./tf-wrapper.sh plan production ``` -1. Run `apply` production. +13. Run `apply` production. ```bash ./tf-wrapper.sh apply production ``` - 1. Push your production branch since development and nonproduction depends it. Because this is a [named environment branch](../docs/FAQ.md#what-is-a-named-branch), - pushing to this branch triggers both _terraform plan_ and _terraform apply_. Review the apply output in your Cloud Build project https://console.cloud.google.com/cloud-build/builds;region=DEFAULT_REGION?project=YOUR_CLOUD_BUILD_PROJECT_ID +14. Push your production branch since development and nonproduction depends it. Because this is a [named environment branch](../docs/FAQ.md#what-is-a-named-branch), +pushing to this branch triggers both _terraform plan_ and _terraform apply_. Review the apply output in your Cloud Build project https://console.cloud.google.com/cloud-build/builds;region=DEFAULT_REGION?project=YOUR_CLOUD_BUILD_PROJECT_ID -*Note:** The Production envrionment must be the first branch to be pushed as it includes the DNS Hub communication that will be used by other environments. +*Note:** The Production environment must be the first branch to be pushed as it includes the DNS Hub communication that will be used by other environments. ```bash git push --set-upstream origin production @@ -332,27 +314,19 @@ See `0-bootstrap` [README-GitHub.md](../0-bootstrap/README-GitHub.md#deploying-s chmod 755 ./tf-wrapper.sh ``` -1. Rename `common.auto.example.tfvars` to `common.auto.tfvars`, rename `production.auto.example.tfvars` to `production.auto.tfvars` and rename `access_context.auto.example.tfvars` to `access_context.auto.tfvars`. +1. Rename `common.auto.example.tfvars` to `common.auto.tfvars`, rename `production.auto.example.tfvars` to `production.auto.tfvars`. ```bash mv common.auto.example.tfvars common.auto.tfvars mv production.auto.example.tfvars production.auto.tfvars - mv access_context.auto.example.tfvars access_context.auto.tfvars ``` 1. Update `common.auto.tfvars` file with values from your environment and bootstrap. See any of the envs folder [README.md](./envs/production/README.md) files for additional information on the values in the `common.auto.tfvars` file. 1. Update `production.auto.tfvars` file with the `target_name_server_addresses`. -1. Update `access_context.auto.tfvars` file with the `access_context_manager_policy_id`. -1. Use `terraform output` to get the backend bucket value from gcp-bootstrap output. +2. Use `terraform output` to get the backend bucket value from gcp-bootstrap output. ```bash - export ORGANIZATION_ID=$(terraform -chdir="../gcp-bootstrap/" output -json common_config | jq '.org_id' --raw-output) - export ACCESS_CONTEXT_MANAGER_ID=$(gcloud access-context-manager policies list --organization ${ORGANIZATION_ID} --format="value(name)") - echo "access_context_manager_policy_id = ${ACCESS_CONTEXT_MANAGER_ID}" - - sed -i'' -e "s/ACCESS_CONTEXT_MANAGER_ID/${ACCESS_CONTEXT_MANAGER_ID}/" ./access_context.auto.tfvars - - export backend_bucket=$(terraform -chdir="../gcp-bootstrap/" output -raw gcs_bucket_tfstate) + export backend_bucket=$(terraform -chdir="../gcp-bootstrap/envs/shared/" output -raw gcs_bucket_tfstate) echo "remote_state_bucket = ${backend_bucket}" sed -i'' -e "s/REMOTE_STATE_BUCKET/${backend_bucket}/" ./common.auto.tfvars @@ -367,10 +341,10 @@ To use the `validate` option of the `tf-wrapper.sh` script, please follow the [i 1. Use `terraform output` to get the Seed project ID and the organization step Terraform service account from 0-bootstrap output. An environment variable `GOOGLE_IMPERSONATE_SERVICE_ACCOUNT` will be set using the Terraform Service Account to enable impersonation. ```bash - export SEED_PROJECT_ID=$(terraform -chdir="../gcp-bootstrap/" output -raw seed_project_id) + export SEED_PROJECT_ID=$(terraform -chdir="../gcp-bootstrap/envs/shared/" output -raw seed_project_id) echo ${SEED_PROJECT_ID} - export GOOGLE_IMPERSONATE_SERVICE_ACCOUNT=$(terraform -chdir="../gcp-bootstrap/" output -raw networks_step_terraform_service_account_email) + export GOOGLE_IMPERSONATE_SERVICE_ACCOUNT=$(terraform -chdir="../gcp-bootstrap/envs/shared/" output -raw networks_step_terraform_service_account_email) echo ${GOOGLE_IMPERSONATE_SERVICE_ACCOUNT} ``` @@ -479,11 +453,3 @@ Before executing the next stages, unset the `GOOGLE_IMPERSONATE_SERVICE_ACCOUNT` ```bash unset GOOGLE_IMPERSONATE_SERVICE_ACCOUNT ``` - -### (Optional) Enforce VPC Service Controls - -Because enabling VPC Service Controls can be a disruptive process, this repo configures VPC Service Controls perimeters in dry run mode by default. This configuration will service traffic that crosses the security perimeter (API requests that originate from inside your perimeter communicating with external resources, or API requests from external resources communicating with resources inside your perimeter) but still allow service traffic normally. - -When you are ready to enforce VPC Service Controls, we recommend that you review the guidance at [Best practices for enabling VPC Service Controls](https://cloud.google.com/vpc-service-controls/docs/enable). After you have added the necessary exceptions and are confident that VPC Service Controls will not disrupt your intended operations, set the variable `enforce_vpcsc` under the module `shared_vpc` to `true` and re-apply this stage. Then re-apply the 4-projects stage, which will inherit the new setting and include those projects inside the enforced perimeter. - -When you need to make changes to an existing enforced perimeter, you can test safely by modifying the configuration of the [dry run perimeter](https://cloud.google.com/vpc-service-controls/docs/dry-run-mode). This will log traffic denied by the dry run perimeter without impacting whether the enforced perimeter allows or denies traffic. diff --git a/3-networks-svpc/access_context.auto.example.tfvars b/3-networks-svpc/access_context.auto.example.tfvars deleted file mode 100644 index 8f8871530..000000000 --- a/3-networks-svpc/access_context.auto.example.tfvars +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright 2021 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. - */ - -access_context_manager_policy_id = ACCESS_CONTEXT_MANAGER_ID diff --git a/3-networks-svpc/common.auto.example.tfvars b/3-networks-svpc/common.auto.example.tfvars index 441314aae..c39ec48dd 100644 --- a/3-networks-svpc/common.auto.example.tfvars +++ b/3-networks-svpc/common.auto.example.tfvars @@ -17,9 +17,4 @@ // The DNS name of peering managed zone. Must end with a period. domain = "example.com." -// Update the following line and add you email in the perimeter_additional_members list. -// You must be in this list to be able to view/access resources in the project protected by the VPC service controls. - -perimeter_additional_members = ["user:YOUR-USER-EMAIL@example.com"] - remote_state_bucket = "REMOTE_STATE_BUCKET" diff --git a/3-networks-svpc/envs/development/README.md b/3-networks-svpc/envs/development/README.md index 92774e3cb..65abc730b 100644 --- a/3-networks-svpc/envs/development/README.md +++ b/3-networks-svpc/envs/development/README.md @@ -15,14 +15,7 @@ The purpose of this step is to set up shared VPCs with default DNS, NAT (optiona | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| access\_context\_manager\_policy\_id | The id of the default Access Context Manager policy created in step `1-org`. Can be obtained by running `gcloud access-context-manager policies list --organization YOUR_ORGANIZATION_ID --format="value(name)"`. | `number` | n/a | yes | | domain | The DNS name of peering managed zone, for instance 'example.com.'. Must end with a period. | `string` | n/a | yes | -| egress\_policies | A list of all [egress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#egress-rules-reference) to use in an enforced perimeter. Each list object has a `from` and `to` value that describes egress\_from and egress\_to.

Example: `[{ from={ identities=[], identity_type="ID_TYPE" }, to={ resources=[], operations={ "SRV_NAME"={ OP_TYPE=[] }}}}]`

Valid Values:
`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`
`SRV_NAME` = "`*`" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)
`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) |
list(object({
from = any
to = any
}))
| `[]` | no | -| egress\_policies\_dry\_run | A list of all [egress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#egress-rules-reference) to use in a dry-run perimeter. Each list object has a `from` and `to` value that describes egress\_from and egress\_to.

Example: `[{ from={ identities=[], identity_type="ID_TYPE" }, to={ resources=[], operations={ "SRV_NAME"={ OP_TYPE=[] }}}}]`

Valid Values:
`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`
`SRV_NAME` = "`*`" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)
`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) |
list(object({
from = any
to = any
}))
| `[]` | no | -| ingress\_policies | A list of all [ingress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#ingress-rules-reference) to use in an enforced perimeter. Each list object has a `from` and `to` value that describes ingress\_from and ingress\_to.

Example: `[{ from={ sources={ resources=[], access_levels=[] }, identities=[], identity_type="ID_TYPE" }, to={ resources=[], operations={ "SRV_NAME"={ OP_TYPE=[] }}}}]`

Valid Values:
`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`
`SRV_NAME` = "`*`" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)
`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) |
list(object({
from = any
to = any
}))
| `[]` | no | -| ingress\_policies\_dry\_run | A list of all [ingress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#ingress-rules-reference) to use in a dry-run perimeter. Each list object has a `from` and `to` value that describes ingress\_from and ingress\_to.

Example: `[{ from={ sources={ resources=[], access_levels=[] }, identities=[], identity_type="ID_TYPE" }, to={ resources=[], operations={ "SRV_NAME"={ OP_TYPE=[] }}}}]`

Valid Values:
`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`
`SRV_NAME` = "`*`" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)
`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) |
list(object({
from = any
to = any
}))
| `[]` | no | -| perimeter\_additional\_members | The list of additional members to be added to the enforced perimeter access level members list. To be able to see the resources protected by the VPC Service Controls in the restricted perimeter, add your user in this list. Entries must be in the standard GCP form: `user:email@example.com` or `serviceAccount:my-service-account@example.com`. | `list(string)` | `[]` | no | -| perimeter\_additional\_members\_dry\_run | The list of additional members to be added to the dry-run perimeter access level members list. To be able to see the resources protected by the VPC Service Controls in the restricted perimeter, add your user in this list. Entries must be in the standard GCP form: `user:email@example.com` or `serviceAccount:my-service-account@example.com`. | `list(string)` | `[]` | no | | remote\_state\_bucket | Backend bucket to load Terraform Remote State Data from previous steps. | `string` | n/a | yes | | tfc\_org\_name | Name of the TFC organization | `string` | `""` | no | @@ -30,13 +23,8 @@ The purpose of this step is to set up shared VPCs with default DNS, NAT (optiona | Name | Description | |------|-------------| -| access\_context\_manager\_policy\_id | Access Context Manager Policy ID. | -| access\_level\_name | Access context manager access level name | -| access\_level\_name\_dry\_run | Access context manager access level name for the dry-run perimeter | -| enforce\_vpcsc | Enable the enforced mode for VPC Service Controls. It is not recommended to enable VPC-SC on the first run deploying your foundation. Review [best practices for enabling VPC Service Controls](https://cloud.google.com/vpc-service-controls/docs/enable), then only enforce the perimeter after you have analyzed the access patterns in your dry-run perimeter and created the necessary exceptions for your use cases. | | network\_name | The name of the VPC being created | | network\_self\_link | The URI of the VPC being created | -| service\_perimeter\_name | Access context manager service perimeter name | | shared\_vpc\_host\_project\_id | The shared vpc host project ID | | subnets\_ips | The IPs and CIDRs of the subnets being created | | subnets\_names | The names of the subnets being created | diff --git a/3-networks-svpc/envs/development/access_context.auto.tfvars b/3-networks-svpc/envs/development/access_context.auto.tfvars deleted file mode 120000 index b0cccce77..000000000 --- a/3-networks-svpc/envs/development/access_context.auto.tfvars +++ /dev/null @@ -1 +0,0 @@ -../../access_context.auto.tfvars \ No newline at end of file diff --git a/3-networks-svpc/envs/development/main.tf b/3-networks-svpc/envs/development/main.tf index d948f1043..53713b601 100644 --- a/3-networks-svpc/envs/development/main.tf +++ b/3-networks-svpc/envs/development/main.tf @@ -46,24 +46,17 @@ locals { module "base_env" { source = "../../modules/base_env" - env = local.env - environment_code = local.environment_code - access_context_manager_policy_id = var.access_context_manager_policy_id - perimeter_additional_members = var.perimeter_additional_members - perimeter_additional_members_dry_run = var.perimeter_additional_members_dry_run - default_region1 = local.default_region1 - default_region2 = local.default_region2 - domain = var.domain - ingress_policies = var.ingress_policies - ingress_policies_dry_run = var.ingress_policies_dry_run - egress_policies = var.egress_policies - egress_policies_dry_run = var.egress_policies_dry_run - enable_partner_interconnect = false - private_service_cidr = local.private_service_cidr - subnet_primary_ranges = local.subnet_primary_ranges - subnet_proxy_ranges = local.subnet_proxy_ranges - subnet_secondary_ranges = local.subnet_secondary_ranges - private_service_connect_ip = "10.17.0.6" - remote_state_bucket = var.remote_state_bucket - tfc_org_name = var.tfc_org_name + env = local.env + environment_code = local.environment_code + default_region1 = local.default_region1 + default_region2 = local.default_region2 + domain = var.domain + enable_partner_interconnect = false + private_service_cidr = local.private_service_cidr + subnet_primary_ranges = local.subnet_primary_ranges + subnet_proxy_ranges = local.subnet_proxy_ranges + subnet_secondary_ranges = local.subnet_secondary_ranges + private_service_connect_ip = "10.17.0.6" + remote_state_bucket = var.remote_state_bucket + tfc_org_name = var.tfc_org_name } diff --git a/3-networks-svpc/envs/development/outputs.tf b/3-networks-svpc/envs/development/outputs.tf index c104444fd..997e266de 100644 --- a/3-networks-svpc/envs/development/outputs.tf +++ b/3-networks-svpc/envs/development/outputs.tf @@ -14,11 +14,6 @@ * limitations under the License. */ -output "access_context_manager_policy_id" { - description = "Access Context Manager Policy ID." - value = var.access_context_manager_policy_id -} - output "shared_vpc_host_project_id" { value = module.base_env.shared_vpc_host_project_id description = "The shared vpc host project ID" @@ -53,23 +48,3 @@ output "subnets_secondary_ranges" { value = module.base_env.subnets_secondary_ranges description = "The secondary ranges associated with these subnets" } - -output "access_level_name" { - value = module.base_env.access_level_name - description = "Access context manager access level name" -} - -output "access_level_name_dry_run" { - value = module.base_env.access_level_name_dry_run - description = "Access context manager access level name for the dry-run perimeter" -} - -output "enforce_vpcsc" { - value = module.base_env.enforce_vpcsc - description = "Enable the enforced mode for VPC Service Controls. It is not recommended to enable VPC-SC on the first run deploying your foundation. Review [best practices for enabling VPC Service Controls](https://cloud.google.com/vpc-service-controls/docs/enable), then only enforce the perimeter after you have analyzed the access patterns in your dry-run perimeter and created the necessary exceptions for your use cases." -} - -output "service_perimeter_name" { - value = module.base_env.service_perimeter_name - description = "Access context manager service perimeter name" -} diff --git a/3-networks-svpc/envs/development/variables.tf b/3-networks-svpc/envs/development/variables.tf index 02448e5a9..a974988da 100644 --- a/3-networks-svpc/envs/development/variables.tf +++ b/3-networks-svpc/envs/development/variables.tf @@ -19,64 +19,11 @@ variable "remote_state_bucket" { type = string } -variable "access_context_manager_policy_id" { - type = number - description = "The id of the default Access Context Manager policy created in step `1-org`. Can be obtained by running `gcloud access-context-manager policies list --organization YOUR_ORGANIZATION_ID --format=\"value(name)\"`." -} - variable "domain" { type = string description = "The DNS name of peering managed zone, for instance 'example.com.'. Must end with a period." } -variable "egress_policies" { - description = "A list of all [egress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#egress-rules-reference) to use in an enforced perimeter. Each list object has a `from` and `to` value that describes egress_from and egress_to.\n\nExample: `[{ from={ identities=[], identity_type=\"ID_TYPE\" }, to={ resources=[], operations={ \"SRV_NAME\"={ OP_TYPE=[] }}}}]`\n\nValid Values:\n`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`\n`SRV_NAME` = \"`*`\" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)\n`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions)" - type = list(object({ - from = any - to = any - })) - default = [] -} - -variable "egress_policies_dry_run" { - description = "A list of all [egress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#egress-rules-reference) to use in a dry-run perimeter. Each list object has a `from` and `to` value that describes egress_from and egress_to.\n\nExample: `[{ from={ identities=[], identity_type=\"ID_TYPE\" }, to={ resources=[], operations={ \"SRV_NAME\"={ OP_TYPE=[] }}}}]`\n\nValid Values:\n`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`\n`SRV_NAME` = \"`*`\" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)\n`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions)" - type = list(object({ - from = any - to = any - })) - default = [] -} - -variable "ingress_policies" { - description = "A list of all [ingress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#ingress-rules-reference) to use in an enforced perimeter. Each list object has a `from` and `to` value that describes ingress_from and ingress_to.\n\nExample: `[{ from={ sources={ resources=[], access_levels=[] }, identities=[], identity_type=\"ID_TYPE\" }, to={ resources=[], operations={ \"SRV_NAME\"={ OP_TYPE=[] }}}}]`\n\nValid Values:\n`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`\n`SRV_NAME` = \"`*`\" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)\n`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions)" - type = list(object({ - from = any - to = any - })) - default = [] -} - -variable "ingress_policies_dry_run" { - description = "A list of all [ingress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#ingress-rules-reference) to use in a dry-run perimeter. Each list object has a `from` and `to` value that describes ingress_from and ingress_to.\n\nExample: `[{ from={ sources={ resources=[], access_levels=[] }, identities=[], identity_type=\"ID_TYPE\" }, to={ resources=[], operations={ \"SRV_NAME\"={ OP_TYPE=[] }}}}]`\n\nValid Values:\n`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`\n`SRV_NAME` = \"`*`\" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)\n`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions)" - type = list(object({ - from = any - to = any - })) - default = [] -} - -variable "perimeter_additional_members" { - description = "The list of additional members to be added to the enforced perimeter access level members list. To be able to see the resources protected by the VPC Service Controls in the restricted perimeter, add your user in this list. Entries must be in the standard GCP form: `user:email@example.com` or `serviceAccount:my-service-account@example.com`." - type = list(string) - default = [] -} - -variable "perimeter_additional_members_dry_run" { - description = "The list of additional members to be added to the dry-run perimeter access level members list. To be able to see the resources protected by the VPC Service Controls in the restricted perimeter, add your user in this list. Entries must be in the standard GCP form: `user:email@example.com` or `serviceAccount:my-service-account@example.com`." - type = list(string) - default = [] -} - variable "tfc_org_name" { description = "Name of the TFC organization" type = string diff --git a/3-networks-svpc/envs/nonproduction/README.md b/3-networks-svpc/envs/nonproduction/README.md index 33377827e..41fe8a615 100644 --- a/3-networks-svpc/envs/nonproduction/README.md +++ b/3-networks-svpc/envs/nonproduction/README.md @@ -15,14 +15,7 @@ The purpose of this step is to set up shared VPCs with default DNS, NAT (optiona | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| access\_context\_manager\_policy\_id | The id of the default Access Context Manager policy created in step `1-org`. Can be obtained by running `gcloud access-context-manager policies list --organization YOUR_ORGANIZATION_ID --format="value(name)"`. | `number` | n/a | yes | | domain | The DNS name of peering managed zone, for instance 'example.com.'. Must end with a period. | `string` | n/a | yes | -| egress\_policies | A list of all [egress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#egress-rules-reference) to use in an enforced perimeter. Each list object has a `from` and `to` value that describes egress\_from and egress\_to.

Example: `[{ from={ identities=[], identity_type="ID_TYPE" }, to={ resources=[], operations={ "SRV_NAME"={ OP_TYPE=[] }}}}]`

Valid Values:
`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`
`SRV_NAME` = "`*`" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)
`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) |
list(object({
from = any
to = any
}))
| `[]` | no | -| egress\_policies\_dry\_run | A list of all [egress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#egress-rules-reference) to use in a dry-run perimeter. Each list object has a `from` and `to` value that describes egress\_from and egress\_to.

Example: `[{ from={ identities=[], identity_type="ID_TYPE" }, to={ resources=[], operations={ "SRV_NAME"={ OP_TYPE=[] }}}}]`

Valid Values:
`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`
`SRV_NAME` = "`*`" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)
`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) |
list(object({
from = any
to = any
}))
| `[]` | no | -| ingress\_policies | A list of all [ingress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#ingress-rules-reference) to use in an enforced perimeter. Each list object has a `from` and `to` value that describes ingress\_from and ingress\_to.

Example: `[{ from={ sources={ resources=[], access_levels=[] }, identities=[], identity_type="ID_TYPE" }, to={ resources=[], operations={ "SRV_NAME"={ OP_TYPE=[] }}}}]`

Valid Values:
`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`
`SRV_NAME` = "`*`" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)
`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) |
list(object({
from = any
to = any
}))
| `[]` | no | -| ingress\_policies\_dry\_run | A list of all [ingress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#ingress-rules-reference) to use in a dry-run perimeter. Each list object has a `from` and `to` value that describes ingress\_from and ingress\_to.

Example: `[{ from={ sources={ resources=[], access_levels=[] }, identities=[], identity_type="ID_TYPE" }, to={ resources=[], operations={ "SRV_NAME"={ OP_TYPE=[] }}}}]`

Valid Values:
`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`
`SRV_NAME` = "`*`" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)
`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) |
list(object({
from = any
to = any
}))
| `[]` | no | -| perimeter\_additional\_members | The list of additional members to be added to the enforced perimeter access level members list. To be able to see the resources protected by the VPC Service Controls in the restricted perimeter, add your user in this list. Entries must be in the standard GCP form: `user:email@example.com` or `serviceAccount:my-service-account@example.com`. | `list(string)` | `[]` | no | -| perimeter\_additional\_members\_dry\_run | The list of additional members to be added to the dry-run perimeter access level members list. To be able to see the resources protected by the VPC Service Controls in the restricted perimeter, add your user in this list. Entries must be in the standard GCP form: `user:email@example.com` or `serviceAccount:my-service-account@example.com`. | `list(string)` | `[]` | no | | remote\_state\_bucket | Backend bucket to load Terraform Remote State Data from previous steps. | `string` | n/a | yes | | tfc\_org\_name | Name of the TFC organization | `string` | `""` | no | @@ -30,13 +23,8 @@ The purpose of this step is to set up shared VPCs with default DNS, NAT (optiona | Name | Description | |------|-------------| -| access\_context\_manager\_policy\_id | Access Context Manager Policy ID. | -| access\_level\_name | Access context manager access level name | -| access\_level\_name\_dry\_run | Access context manager access level name for the dry-run perimeter | -| enforce\_vpcsc | Enable the enforced mode for VPC Service Controls. It is not recommended to enable VPC-SC on the first run deploying your foundation. Review [best practices for enabling VPC Service Controls](https://cloud.google.com/vpc-service-controls/docs/enable), then only enforce the perimeter after you have analyzed the access patterns in your dry-run perimeter and created the necessary exceptions for your use cases. | | network\_name | The name of the VPC being created | | network\_self\_link | The URI of the VPC being created | -| service\_perimeter\_name | Access context manager service perimeter name | | shared\_vpc\_host\_project\_id | The shared vpc host project ID | | subnets\_ips | The IPs and CIDRs of the subnets being created | | subnets\_names | The names of the subnets being created | diff --git a/3-networks-svpc/envs/nonproduction/access_context.auto.tfvars b/3-networks-svpc/envs/nonproduction/access_context.auto.tfvars deleted file mode 120000 index b0cccce77..000000000 --- a/3-networks-svpc/envs/nonproduction/access_context.auto.tfvars +++ /dev/null @@ -1 +0,0 @@ -../../access_context.auto.tfvars \ No newline at end of file diff --git a/3-networks-svpc/envs/nonproduction/main.tf b/3-networks-svpc/envs/nonproduction/main.tf index 2686cf532..8dcc6afa4 100644 --- a/3-networks-svpc/envs/nonproduction/main.tf +++ b/3-networks-svpc/envs/nonproduction/main.tf @@ -44,24 +44,17 @@ locals { module "base_env" { source = "../../modules/base_env" - env = local.env - environment_code = local.environment_code - access_context_manager_policy_id = var.access_context_manager_policy_id - perimeter_additional_members = var.perimeter_additional_members - perimeter_additional_members_dry_run = var.perimeter_additional_members_dry_run - default_region1 = local.default_region1 - default_region2 = local.default_region2 - domain = var.domain - ingress_policies = var.ingress_policies - ingress_policies_dry_run = var.ingress_policies_dry_run - egress_policies = var.egress_policies - egress_policies_dry_run = var.egress_policies_dry_run - enable_partner_interconnect = false - private_service_cidr = local.private_service_cidr - subnet_proxy_ranges = local.subnet_proxy_ranges - subnet_primary_ranges = local.subnet_primary_ranges - subnet_secondary_ranges = local.subnet_secondary_ranges - private_service_connect_ip = "10.17.0.7" - remote_state_bucket = var.remote_state_bucket - tfc_org_name = var.tfc_org_name + env = local.env + environment_code = local.environment_code + default_region1 = local.default_region1 + default_region2 = local.default_region2 + domain = var.domain + enable_partner_interconnect = false + private_service_cidr = local.private_service_cidr + subnet_proxy_ranges = local.subnet_proxy_ranges + subnet_primary_ranges = local.subnet_primary_ranges + subnet_secondary_ranges = local.subnet_secondary_ranges + private_service_connect_ip = "10.17.0.7" + remote_state_bucket = var.remote_state_bucket + tfc_org_name = var.tfc_org_name } diff --git a/3-networks-svpc/envs/nonproduction/outputs.tf b/3-networks-svpc/envs/nonproduction/outputs.tf index c104444fd..997e266de 100644 --- a/3-networks-svpc/envs/nonproduction/outputs.tf +++ b/3-networks-svpc/envs/nonproduction/outputs.tf @@ -14,11 +14,6 @@ * limitations under the License. */ -output "access_context_manager_policy_id" { - description = "Access Context Manager Policy ID." - value = var.access_context_manager_policy_id -} - output "shared_vpc_host_project_id" { value = module.base_env.shared_vpc_host_project_id description = "The shared vpc host project ID" @@ -53,23 +48,3 @@ output "subnets_secondary_ranges" { value = module.base_env.subnets_secondary_ranges description = "The secondary ranges associated with these subnets" } - -output "access_level_name" { - value = module.base_env.access_level_name - description = "Access context manager access level name" -} - -output "access_level_name_dry_run" { - value = module.base_env.access_level_name_dry_run - description = "Access context manager access level name for the dry-run perimeter" -} - -output "enforce_vpcsc" { - value = module.base_env.enforce_vpcsc - description = "Enable the enforced mode for VPC Service Controls. It is not recommended to enable VPC-SC on the first run deploying your foundation. Review [best practices for enabling VPC Service Controls](https://cloud.google.com/vpc-service-controls/docs/enable), then only enforce the perimeter after you have analyzed the access patterns in your dry-run perimeter and created the necessary exceptions for your use cases." -} - -output "service_perimeter_name" { - value = module.base_env.service_perimeter_name - description = "Access context manager service perimeter name" -} diff --git a/3-networks-svpc/envs/nonproduction/variables.tf b/3-networks-svpc/envs/nonproduction/variables.tf index 02448e5a9..a974988da 100644 --- a/3-networks-svpc/envs/nonproduction/variables.tf +++ b/3-networks-svpc/envs/nonproduction/variables.tf @@ -19,64 +19,11 @@ variable "remote_state_bucket" { type = string } -variable "access_context_manager_policy_id" { - type = number - description = "The id of the default Access Context Manager policy created in step `1-org`. Can be obtained by running `gcloud access-context-manager policies list --organization YOUR_ORGANIZATION_ID --format=\"value(name)\"`." -} - variable "domain" { type = string description = "The DNS name of peering managed zone, for instance 'example.com.'. Must end with a period." } -variable "egress_policies" { - description = "A list of all [egress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#egress-rules-reference) to use in an enforced perimeter. Each list object has a `from` and `to` value that describes egress_from and egress_to.\n\nExample: `[{ from={ identities=[], identity_type=\"ID_TYPE\" }, to={ resources=[], operations={ \"SRV_NAME\"={ OP_TYPE=[] }}}}]`\n\nValid Values:\n`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`\n`SRV_NAME` = \"`*`\" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)\n`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions)" - type = list(object({ - from = any - to = any - })) - default = [] -} - -variable "egress_policies_dry_run" { - description = "A list of all [egress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#egress-rules-reference) to use in a dry-run perimeter. Each list object has a `from` and `to` value that describes egress_from and egress_to.\n\nExample: `[{ from={ identities=[], identity_type=\"ID_TYPE\" }, to={ resources=[], operations={ \"SRV_NAME\"={ OP_TYPE=[] }}}}]`\n\nValid Values:\n`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`\n`SRV_NAME` = \"`*`\" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)\n`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions)" - type = list(object({ - from = any - to = any - })) - default = [] -} - -variable "ingress_policies" { - description = "A list of all [ingress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#ingress-rules-reference) to use in an enforced perimeter. Each list object has a `from` and `to` value that describes ingress_from and ingress_to.\n\nExample: `[{ from={ sources={ resources=[], access_levels=[] }, identities=[], identity_type=\"ID_TYPE\" }, to={ resources=[], operations={ \"SRV_NAME\"={ OP_TYPE=[] }}}}]`\n\nValid Values:\n`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`\n`SRV_NAME` = \"`*`\" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)\n`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions)" - type = list(object({ - from = any - to = any - })) - default = [] -} - -variable "ingress_policies_dry_run" { - description = "A list of all [ingress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#ingress-rules-reference) to use in a dry-run perimeter. Each list object has a `from` and `to` value that describes ingress_from and ingress_to.\n\nExample: `[{ from={ sources={ resources=[], access_levels=[] }, identities=[], identity_type=\"ID_TYPE\" }, to={ resources=[], operations={ \"SRV_NAME\"={ OP_TYPE=[] }}}}]`\n\nValid Values:\n`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`\n`SRV_NAME` = \"`*`\" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)\n`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions)" - type = list(object({ - from = any - to = any - })) - default = [] -} - -variable "perimeter_additional_members" { - description = "The list of additional members to be added to the enforced perimeter access level members list. To be able to see the resources protected by the VPC Service Controls in the restricted perimeter, add your user in this list. Entries must be in the standard GCP form: `user:email@example.com` or `serviceAccount:my-service-account@example.com`." - type = list(string) - default = [] -} - -variable "perimeter_additional_members_dry_run" { - description = "The list of additional members to be added to the dry-run perimeter access level members list. To be able to see the resources protected by the VPC Service Controls in the restricted perimeter, add your user in this list. Entries must be in the standard GCP form: `user:email@example.com` or `serviceAccount:my-service-account@example.com`." - type = list(string) - default = [] -} - variable "tfc_org_name" { description = "Name of the TFC organization" type = string diff --git a/3-networks-svpc/envs/production/README.md b/3-networks-svpc/envs/production/README.md index 01a596fbd..b17c783f1 100644 --- a/3-networks-svpc/envs/production/README.md +++ b/3-networks-svpc/envs/production/README.md @@ -15,14 +15,7 @@ The purpose of this step is to set up shared VPCs with default DNS, NAT (optiona | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| access\_context\_manager\_policy\_id | The id of the default Access Context Manager policy created in step `1-org`. Can be obtained by running `gcloud access-context-manager policies list --organization YOUR_ORGANIZATION_ID --format="value(name)"`. | `number` | n/a | yes | | domain | The DNS name of peering managed zone, for instance 'example.com.'. Must end with a period. | `string` | n/a | yes | -| egress\_policies | A list of all [egress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#egress-rules-reference) to use in an enforced perimeter. Each list object has a `from` and `to` value that describes egress\_from and egress\_to.

Example: `[{ from={ identities=[], identity_type="ID_TYPE" }, to={ resources=[], operations={ "SRV_NAME"={ OP_TYPE=[] }}}}]`

Valid Values:
`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`
`SRV_NAME` = "`*`" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)
`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) |
list(object({
from = any
to = any
}))
| `[]` | no | -| egress\_policies\_dry\_run | A list of all [egress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#egress-rules-reference) to use in a dry-run perimeter. Each list object has a `from` and `to` value that describes egress\_from and egress\_to.

Example: `[{ from={ identities=[], identity_type="ID_TYPE" }, to={ resources=[], operations={ "SRV_NAME"={ OP_TYPE=[] }}}}]`

Valid Values:
`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`
`SRV_NAME` = "`*`" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)
`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) |
list(object({
from = any
to = any
}))
| `[]` | no | -| ingress\_policies | A list of all [ingress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#ingress-rules-reference) to use in an enforced perimeter. Each list object has a `from` and `to` value that describes ingress\_from and ingress\_to.

Example: `[{ from={ sources={ resources=[], access_levels=[] }, identities=[], identity_type="ID_TYPE" }, to={ resources=[], operations={ "SRV_NAME"={ OP_TYPE=[] }}}}]`

Valid Values:
`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`
`SRV_NAME` = "`*`" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)
`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) |
list(object({
from = any
to = any
}))
| `[]` | no | -| ingress\_policies\_dry\_run | A list of all [ingress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#ingress-rules-reference) to use in a dry-run perimeter. Each list object has a `from` and `to` value that describes ingress\_from and ingress\_to.

Example: `[{ from={ sources={ resources=[], access_levels=[] }, identities=[], identity_type="ID_TYPE" }, to={ resources=[], operations={ "SRV_NAME"={ OP_TYPE=[] }}}}]`

Valid Values:
`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`
`SRV_NAME` = "`*`" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)
`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) |
list(object({
from = any
to = any
}))
| `[]` | no | -| perimeter\_additional\_members | The list of additional members to be added to the enforced perimeter access level members list. To be able to see the resources protected by the VPC Service Controls in the restricted perimeter, add your user in this list. Entries must be in the standard GCP form: `user:email@example.com` or `serviceAccount:my-service-account@example.com`. | `list(string)` | `[]` | no | -| perimeter\_additional\_members\_dry\_run | The list of additional members to be added to the dry-run perimeter access level members list. To be able to see the resources protected by the VPC Service Controls in the restricted perimeter, add your user in this list. Entries must be in the standard GCP form: `user:email@example.com` or `serviceAccount:my-service-account@example.com`. | `list(string)` | `[]` | no | | remote\_state\_bucket | Backend bucket to load Terraform Remote State Data from previous steps. | `string` | n/a | yes | | target\_name\_server\_addresses | List of IPv4 address of target name servers for the forwarding zone configuration. See https://cloud.google.com/dns/docs/overview#dns-forwarding-zones for details on target name servers in the context of Cloud DNS forwarding zones. | `list(map(any))` | `[]` | no | | tfc\_org\_name | Name of the TFC organization | `string` | `""` | no | @@ -31,13 +24,8 @@ The purpose of this step is to set up shared VPCs with default DNS, NAT (optiona | Name | Description | |------|-------------| -| access\_context\_manager\_policy\_id | Access Context Manager Policy ID. | -| access\_level\_name | Access context manager access level name | -| access\_level\_name\_dry\_run | Access context manager access level name for the dry-run perimeter | -| enforce\_vpcsc | Enable the enforced mode for VPC Service Controls. It is not recommended to enable VPC-SC on the first run deploying your foundation. Review [best practices for enabling VPC Service Controls](https://cloud.google.com/vpc-service-controls/docs/enable), then only enforce the perimeter after you have analyzed the access patterns in your dry-run perimeter and created the necessary exceptions for your use cases. | | network\_name | The name of the VPC being created | | network\_self\_link | The URI of the VPC being created | -| service\_perimeter\_name | Access context manager service perimeter name | | shared\_vpc\_host\_project\_id | The shared vpc host project ID | | subnets\_ips | The IPs and CIDRs of the subnets being created | | subnets\_names | The names of the subnets being created | diff --git a/3-networks-svpc/envs/production/access_context.auto.tfvars b/3-networks-svpc/envs/production/access_context.auto.tfvars deleted file mode 120000 index b0cccce77..000000000 --- a/3-networks-svpc/envs/production/access_context.auto.tfvars +++ /dev/null @@ -1 +0,0 @@ -../../access_context.auto.tfvars \ No newline at end of file diff --git a/3-networks-svpc/envs/production/main.tf b/3-networks-svpc/envs/production/main.tf index 07e043eb1..57b04b5c0 100644 --- a/3-networks-svpc/envs/production/main.tf +++ b/3-networks-svpc/envs/production/main.tf @@ -44,25 +44,18 @@ locals { module "base_env" { source = "../../modules/base_env" - env = local.env - environment_code = local.environment_code - access_context_manager_policy_id = var.access_context_manager_policy_id - perimeter_additional_members = var.perimeter_additional_members - perimeter_additional_members_dry_run = var.perimeter_additional_members_dry_run - default_region1 = local.default_region1 - default_region2 = local.default_region2 - domain = var.domain - ingress_policies = var.ingress_policies - ingress_policies_dry_run = var.ingress_policies_dry_run - egress_policies = var.egress_policies - egress_policies_dry_run = var.egress_policies_dry_run - enable_partner_interconnect = false - private_service_cidr = local.private_service_cidr - subnet_primary_ranges = local.subnet_primary_ranges - subnet_proxy_ranges = local.subnet_proxy_ranges - subnet_secondary_ranges = local.subnet_secondary_ranges - private_service_connect_ip = "10.17.0.8" - remote_state_bucket = var.remote_state_bucket - tfc_org_name = var.tfc_org_name - target_name_server_addresses = var.target_name_server_addresses + env = local.env + environment_code = local.environment_code + default_region1 = local.default_region1 + default_region2 = local.default_region2 + domain = var.domain + enable_partner_interconnect = false + private_service_cidr = local.private_service_cidr + subnet_primary_ranges = local.subnet_primary_ranges + subnet_proxy_ranges = local.subnet_proxy_ranges + subnet_secondary_ranges = local.subnet_secondary_ranges + private_service_connect_ip = "10.17.0.8" + remote_state_bucket = var.remote_state_bucket + tfc_org_name = var.tfc_org_name + target_name_server_addresses = var.target_name_server_addresses } diff --git a/3-networks-svpc/envs/production/outputs.tf b/3-networks-svpc/envs/production/outputs.tf index c104444fd..997e266de 100644 --- a/3-networks-svpc/envs/production/outputs.tf +++ b/3-networks-svpc/envs/production/outputs.tf @@ -14,11 +14,6 @@ * limitations under the License. */ -output "access_context_manager_policy_id" { - description = "Access Context Manager Policy ID." - value = var.access_context_manager_policy_id -} - output "shared_vpc_host_project_id" { value = module.base_env.shared_vpc_host_project_id description = "The shared vpc host project ID" @@ -53,23 +48,3 @@ output "subnets_secondary_ranges" { value = module.base_env.subnets_secondary_ranges description = "The secondary ranges associated with these subnets" } - -output "access_level_name" { - value = module.base_env.access_level_name - description = "Access context manager access level name" -} - -output "access_level_name_dry_run" { - value = module.base_env.access_level_name_dry_run - description = "Access context manager access level name for the dry-run perimeter" -} - -output "enforce_vpcsc" { - value = module.base_env.enforce_vpcsc - description = "Enable the enforced mode for VPC Service Controls. It is not recommended to enable VPC-SC on the first run deploying your foundation. Review [best practices for enabling VPC Service Controls](https://cloud.google.com/vpc-service-controls/docs/enable), then only enforce the perimeter after you have analyzed the access patterns in your dry-run perimeter and created the necessary exceptions for your use cases." -} - -output "service_perimeter_name" { - value = module.base_env.service_perimeter_name - description = "Access context manager service perimeter name" -} diff --git a/3-networks-svpc/envs/production/variables.tf b/3-networks-svpc/envs/production/variables.tf index 588a9e69d..cbcd38e28 100644 --- a/3-networks-svpc/envs/production/variables.tf +++ b/3-networks-svpc/envs/production/variables.tf @@ -25,64 +25,11 @@ variable "remote_state_bucket" { type = string } -variable "access_context_manager_policy_id" { - type = number - description = "The id of the default Access Context Manager policy created in step `1-org`. Can be obtained by running `gcloud access-context-manager policies list --organization YOUR_ORGANIZATION_ID --format=\"value(name)\"`." -} - variable "domain" { type = string description = "The DNS name of peering managed zone, for instance 'example.com.'. Must end with a period." } -variable "egress_policies" { - description = "A list of all [egress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#egress-rules-reference) to use in an enforced perimeter. Each list object has a `from` and `to` value that describes egress_from and egress_to.\n\nExample: `[{ from={ identities=[], identity_type=\"ID_TYPE\" }, to={ resources=[], operations={ \"SRV_NAME\"={ OP_TYPE=[] }}}}]`\n\nValid Values:\n`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`\n`SRV_NAME` = \"`*`\" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)\n`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions)" - type = list(object({ - from = any - to = any - })) - default = [] -} - -variable "egress_policies_dry_run" { - description = "A list of all [egress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#egress-rules-reference) to use in a dry-run perimeter. Each list object has a `from` and `to` value that describes egress_from and egress_to.\n\nExample: `[{ from={ identities=[], identity_type=\"ID_TYPE\" }, to={ resources=[], operations={ \"SRV_NAME\"={ OP_TYPE=[] }}}}]`\n\nValid Values:\n`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`\n`SRV_NAME` = \"`*`\" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)\n`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions)" - type = list(object({ - from = any - to = any - })) - default = [] -} - -variable "ingress_policies" { - description = "A list of all [ingress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#ingress-rules-reference) to use in an enforced perimeter. Each list object has a `from` and `to` value that describes ingress_from and ingress_to.\n\nExample: `[{ from={ sources={ resources=[], access_levels=[] }, identities=[], identity_type=\"ID_TYPE\" }, to={ resources=[], operations={ \"SRV_NAME\"={ OP_TYPE=[] }}}}]`\n\nValid Values:\n`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`\n`SRV_NAME` = \"`*`\" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)\n`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions)" - type = list(object({ - from = any - to = any - })) - default = [] -} - -variable "ingress_policies_dry_run" { - description = "A list of all [ingress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#ingress-rules-reference) to use in a dry-run perimeter. Each list object has a `from` and `to` value that describes ingress_from and ingress_to.\n\nExample: `[{ from={ sources={ resources=[], access_levels=[] }, identities=[], identity_type=\"ID_TYPE\" }, to={ resources=[], operations={ \"SRV_NAME\"={ OP_TYPE=[] }}}}]`\n\nValid Values:\n`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`\n`SRV_NAME` = \"`*`\" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)\n`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions)" - type = list(object({ - from = any - to = any - })) - default = [] -} - -variable "perimeter_additional_members" { - description = "The list of additional members to be added to the enforced perimeter access level members list. To be able to see the resources protected by the VPC Service Controls in the restricted perimeter, add your user in this list. Entries must be in the standard GCP form: `user:email@example.com` or `serviceAccount:my-service-account@example.com`." - type = list(string) - default = [] -} - -variable "perimeter_additional_members_dry_run" { - description = "The list of additional members to be added to the dry-run perimeter access level members list. To be able to see the resources protected by the VPC Service Controls in the restricted perimeter, add your user in this list. Entries must be in the standard GCP form: `user:email@example.com` or `serviceAccount:my-service-account@example.com`." - type = list(string) - default = [] -} - variable "tfc_org_name" { description = "Name of the TFC organization" type = string diff --git a/3-networks-svpc/envs/shared/access_context.auto.tfvars b/3-networks-svpc/envs/shared/access_context.auto.tfvars deleted file mode 120000 index b0cccce77..000000000 --- a/3-networks-svpc/envs/shared/access_context.auto.tfvars +++ /dev/null @@ -1 +0,0 @@ -../../access_context.auto.tfvars \ No newline at end of file diff --git a/3-networks-svpc/modules/base_env/README.md b/3-networks-svpc/modules/base_env/README.md index 928e11794..518f0821d 100644 --- a/3-networks-svpc/modules/base_env/README.md +++ b/3-networks-svpc/modules/base_env/README.md @@ -3,22 +3,13 @@ | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| access\_context\_manager\_policy\_id | The id of the default Access Context Manager policy created in step `1-org`. Can be obtained by running `gcloud access-context-manager policies list --organization YOUR_ORGANIZATION_ID --format="value(name)"`. | `number` | n/a | yes | -| custom\_restricted\_services | List of custom services to be protected by the enforced VPC-SC perimeter. If empty, all supported services (https://cloud.google.com/vpc-service-controls/docs/supported-products) will be protected. | `list(string)` | `[]` | no | -| custom\_restricted\_services\_dry\_run | List of custom services to be protected by the dry-run VPC-SC perimeter. If empty, all supported services (https://cloud.google.com/vpc-service-controls/docs/supported-products) will be protected. | `list(string)` | `[]` | no | | default\_region1 | First subnet region. The shared vpc modules only configures two regions. | `string` | n/a | yes | | default\_region2 | Second subnet region. The shared vpc modules only configures two regions. | `string` | n/a | yes | | domain | The DNS name of peering managed zone, for instance 'example.com.'. Must end with a period. | `string` | n/a | yes | -| egress\_policies | A list of all [egress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#egress-rules-reference) to use in an enforced perimeter. Each list object has a `from` and `to` value that describes egress\_from and egress\_to.

Example: `[{ from={ identities=[], identity_type="ID_TYPE" }, to={ resources=[], operations={ "SRV_NAME"={ OP_TYPE=[] }}}}]`

Valid Values:
`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`
`SRV_NAME` = "`*`" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)
`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) |
list(object({
from = any
to = any
}))
| `[]` | no | -| egress\_policies\_dry\_run | A list of all [egress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#egress-rules-reference) to use in a dry-run perimeter. Each list object has a `from` and `to` value that describes egress\_from and egress\_to.

Example: `[{ from={ identities=[], identity_type="ID_TYPE" }, to={ resources=[], operations={ "SRV_NAME"={ OP_TYPE=[] }}}}]`

Valid Values:
`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`
`SRV_NAME` = "`*`" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)
`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) |
list(object({
from = any
to = any
}))
| `[]` | no | | enable\_dedicated\_interconnect | Enable Dedicated Interconnect in the environment. | `bool` | `false` | no | | enable\_partner\_interconnect | Enable Partner Interconnect in the environment. | `bool` | `false` | no | | env | The environment to prepare (ex. development) | `string` | n/a | yes | | environment\_code | A short form of the folder level resources (environment) within the Google Cloud organization (ex. d). | `string` | n/a | yes | -| ingress\_policies | A list of all [ingress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#ingress-rules-reference) to use in an enforced perimeter. Each list object has a `from` and `to` value that describes ingress\_from and ingress\_to.

Example: `[{ from={ sources={ resources=[], access_levels=[] }, identities=[], identity_type="ID_TYPE" }, to={ resources=[], operations={ "SRV_NAME"={ OP_TYPE=[] }}}}]`

Valid Values:
`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`
`SRV_NAME` = "`*`" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)
`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) |
list(object({
from = any
to = any
}))
| `[]` | no | -| ingress\_policies\_dry\_run | A list of all [ingress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#ingress-rules-reference) to use in a dry-run perimeter. Each list object has a `from` and `to` value that describes ingress\_from and ingress\_to.

Example: `[{ from={ sources={ resources=[], access_levels=[] }, identities=[], identity_type="ID_TYPE" }, to={ resources=[], operations={ "SRV_NAME"={ OP_TYPE=[] }}}}]`

Valid Values:
`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`
`SRV_NAME` = "`*`" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)
`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) |
list(object({
from = any
to = any
}))
| `[]` | no | -| perimeter\_additional\_members | The list of additional members to be added to the enforced perimeter access level members list. To be able to see the resources protected by the VPC Service Controls in the restricted perimeter, add your user in this list. Entries must be in the standard GCP form: `user:email@example.com` or `serviceAccount:my-service-account@example.com`. | `list(string)` | `[]` | no | -| perimeter\_additional\_members\_dry\_run | The list of additional members to be added to the dry-run perimeter access level members list. To be able to see the resources protected by the VPC Service Controls in the restricted perimeter, add your user in this list. Entries must be in the standard GCP form: `user:email@example.com` or `serviceAccount:my-service-account@example.com`. | `list(string)` | `[]` | no | | private\_service\_cidr | CIDR range for private service networking. Used for Cloud SQL and other managed services in the Shared Vpc. | `string` | n/a | yes | | private\_service\_connect\_ip | The base subnet internal IP to be used as the private service connect endpoint in the Shared VPC | `string` | n/a | yes | | remote\_state\_bucket | Backend bucket to load Terraform Remote State Data from previous steps. | `string` | n/a | yes | @@ -33,12 +24,8 @@ | Name | Description | |------|-------------| -| access\_level\_name | Access context manager access level name for the enforced perimeter | -| access\_level\_name\_dry\_run | Access context manager access level name for the dry-run perimeter | -| enforce\_vpcsc | Enable the enforced mode for VPC Service Controls. It is not recommended to enable VPC-SC on the first run deploying your foundation. Review [best practices for enabling VPC Service Controls](https://cloud.google.com/vpc-service-controls/docs/enable), then only enforce the perimeter after you have analyzed the access patterns in your dry-run perimeter and created the necessary exceptions for your use cases. | | network\_name | The name of the VPC being created | | network\_self\_link | The URI of the VPC being created | -| service\_perimeter\_name | Access context manager service perimeter name for the enforced perimeter | | shared\_vpc\_host\_project\_id | The shared vpc host project ID | | subnets\_ips | The IPs and CIDRs of the subnets being created | | subnets\_names | The names of the subnets being created | diff --git a/3-networks-svpc/modules/base_env/main.tf b/3-networks-svpc/modules/base_env/main.tf index e94e5893e..93b1d2c97 100644 --- a/3-networks-svpc/modules/base_env/main.tf +++ b/3-networks-svpc/modules/base_env/main.tf @@ -15,153 +15,7 @@ */ locals { - dedicated_interconnect_egress_policy = var.enable_dedicated_interconnect ? [ - { - "from" = { - "identity_type" = "" - "identities" = ["serviceAccount:${local.networks_service_account}"] - }, - "to" = { - "resources" = ["projects/${local.interconnect_project_number}"] - "operations" = { - "compute.googleapis.com" = { - "methods" = ["*"] - } - } - } - }, - ] : [] - bgp_asn_number = var.enable_partner_interconnect ? "16550" : "64514" - - supported_restricted_service = [ - "accessapproval.googleapis.com", - "adsdatahub.googleapis.com", - "aiplatform.googleapis.com", - "alloydb.googleapis.com", - "documentai.googleapis.com", - "analyticshub.googleapis.com", - "apigee.googleapis.com", - "apigeeconnect.googleapis.com", - "artifactregistry.googleapis.com", - "assuredworkloads.googleapis.com", - "automl.googleapis.com", - "baremetalsolution.googleapis.com", - "batch.googleapis.com", - "bigquery.googleapis.com", - "bigquerydatapolicy.googleapis.com", - "bigquerydatatransfer.googleapis.com", - "bigquerymigration.googleapis.com", - "bigqueryreservation.googleapis.com", - "bigtable.googleapis.com", - "binaryauthorization.googleapis.com", - "cloud.googleapis.com", - "cloudasset.googleapis.com", - "cloudbuild.googleapis.com", - "clouddebugger.googleapis.com", - "clouddeploy.googleapis.com", - "clouderrorreporting.googleapis.com", - "cloudfunctions.googleapis.com", - "cloudkms.googleapis.com", - "cloudprofiler.googleapis.com", - "cloudresourcemanager.googleapis.com", - "cloudscheduler.googleapis.com", - "cloudsearch.googleapis.com", - "cloudtrace.googleapis.com", - "composer.googleapis.com", - "compute.googleapis.com", - "confidentialcomputing.googleapis.com", - "connectgateway.googleapis.com", - "contactcenterinsights.googleapis.com", - "container.googleapis.com", - "containeranalysis.googleapis.com", - "containerfilesystem.googleapis.com", - "containerregistry.googleapis.com", - "containerthreatdetection.googleapis.com", - "datacatalog.googleapis.com", - "dataflow.googleapis.com", - "datafusion.googleapis.com", - "datamigration.googleapis.com", - "dataplex.googleapis.com", - "dataproc.googleapis.com", - "datastream.googleapis.com", - "dialogflow.googleapis.com", - "dlp.googleapis.com", - "dns.googleapis.com", - "documentai.googleapis.com", - "domains.googleapis.com", - "eventarc.googleapis.com", - "file.googleapis.com", - "firebaseappcheck.googleapis.com", - "firebaserules.googleapis.com", - "firestore.googleapis.com", - "gameservices.googleapis.com", - "gkebackup.googleapis.com", - "gkeconnect.googleapis.com", - "gkehub.googleapis.com", - "healthcare.googleapis.com", - "iam.googleapis.com", - "iamcredentials.googleapis.com", - "iaptunnel.googleapis.com", - "ids.googleapis.com", - "integrations.googleapis.com", - "kmsinventory.googleapis.com", - "krmapihosting.googleapis.com", - "language.googleapis.com", - "lifesciences.googleapis.com", - "logging.googleapis.com", - "managedidentities.googleapis.com", - "memcache.googleapis.com", - "meshca.googleapis.com", - "meshconfig.googleapis.com", - "metastore.googleapis.com", - "ml.googleapis.com", - "monitoring.googleapis.com", - "networkconnectivity.googleapis.com", - "networkmanagement.googleapis.com", - "networksecurity.googleapis.com", - "networkservices.googleapis.com", - "notebooks.googleapis.com", - "opsconfigmonitoring.googleapis.com", - "orgpolicy.googleapis.com", - "osconfig.googleapis.com", - "oslogin.googleapis.com", - "privateca.googleapis.com", - "pubsub.googleapis.com", - "pubsublite.googleapis.com", - "recaptchaenterprise.googleapis.com", - "recommender.googleapis.com", - "redis.googleapis.com", - "retail.googleapis.com", - "run.googleapis.com", - "secretmanager.googleapis.com", - "servicecontrol.googleapis.com", - "servicedirectory.googleapis.com", - "spanner.googleapis.com", - "speakerid.googleapis.com", - "speech.googleapis.com", - "sqladmin.googleapis.com", - "storage.googleapis.com", - "storagetransfer.googleapis.com", - "sts.googleapis.com", - "texttospeech.googleapis.com", - "timeseriesinsights.googleapis.com", - "tpu.googleapis.com", - "trafficdirector.googleapis.com", - "transcoder.googleapis.com", - "translate.googleapis.com", - "videointelligence.googleapis.com", - "vision.googleapis.com", - "visionai.googleapis.com", - "vmmigration.googleapis.com", - "vpcaccess.googleapis.com", - "webrisk.googleapis.com", - "workflows.googleapis.com", - "workstations.googleapis.com", - ] - - restricted_services = length(var.custom_restricted_services) != 0 ? var.custom_restricted_services : local.supported_restricted_service - restricted_services_dry_run = length(var.custom_restricted_services_dry_run) != 0 ? var.custom_restricted_services : local.supported_restricted_service } /****************************************** @@ -170,39 +24,16 @@ locals { module "shared_vpc" { source = "../shared_vpc" - project_id = local.shared_vpc_project_id - project_number = local.shared_vpc_project_number - dns_project_id = local.dns_project_id - environment_code = var.environment_code - access_context_manager_policy_id = var.access_context_manager_policy_id - restricted_services = local.restricted_services - restricted_services_dry_run = local.restricted_services_dry_run - members = distinct(concat([ - "serviceAccount:${local.networks_service_account}", - "serviceAccount:${local.projects_service_account}", - "serviceAccount:${local.organization_service_account}", - ], var.perimeter_additional_members)) - members_dry_run = distinct(concat([ - "serviceAccount:${local.networks_service_account}", - "serviceAccount:${local.projects_service_account}", - "serviceAccount:${local.organization_service_account}", - ], var.perimeter_additional_members)) - private_service_cidr = var.private_service_cidr - private_service_connect_ip = var.private_service_connect_ip - bgp_asn_subnet = local.bgp_asn_number - default_region1 = var.default_region1 - default_region2 = var.default_region2 - domain = var.domain - ingress_policies = var.ingress_policies - ingress_policies_dry_run = var.ingress_policies_dry_run - egress_policies = distinct(concat( - local.dedicated_interconnect_egress_policy, - var.egress_policies - )) - egress_policies_dry_run = distinct(concat( - local.dedicated_interconnect_egress_policy, - var.egress_policies_dry_run - )) + project_id = local.shared_vpc_project_id + project_number = local.shared_vpc_project_number + dns_project_id = local.dns_project_id + environment_code = var.environment_code + private_service_cidr = var.private_service_cidr + private_service_connect_ip = var.private_service_connect_ip + bgp_asn_subnet = local.bgp_asn_number + default_region1 = var.default_region1 + default_region2 = var.default_region2 + domain = var.domain target_name_server_addresses = var.target_name_server_addresses @@ -257,4 +88,3 @@ module "shared_vpc" { "sb-${var.environment_code}-svpc-${var.default_region1}" = var.subnet_secondary_ranges[var.default_region1] } } - diff --git a/3-networks-svpc/modules/base_env/outputs.tf b/3-networks-svpc/modules/base_env/outputs.tf index c55a4771c..49b75b865 100644 --- a/3-networks-svpc/modules/base_env/outputs.tf +++ b/3-networks-svpc/modules/base_env/outputs.tf @@ -53,23 +53,3 @@ output "subnets_secondary_ranges" { value = module.shared_vpc.subnets_secondary_ranges description = "The secondary ranges associated with these subnets" } - -output "access_level_name" { - value = module.shared_vpc.access_level_name - description = "Access context manager access level name for the enforced perimeter" -} - -output "access_level_name_dry_run" { - value = module.shared_vpc.access_level_name_dry_run - description = "Access context manager access level name for the dry-run perimeter" -} - -output "enforce_vpcsc" { - value = module.shared_vpc.enforce_vpcsc - description = "Enable the enforced mode for VPC Service Controls. It is not recommended to enable VPC-SC on the first run deploying your foundation. Review [best practices for enabling VPC Service Controls](https://cloud.google.com/vpc-service-controls/docs/enable), then only enforce the perimeter after you have analyzed the access patterns in your dry-run perimeter and created the necessary exceptions for your use cases." -} - -output "service_perimeter_name" { - value = module.shared_vpc.service_perimeter_name - description = "Access context manager service perimeter name for the enforced perimeter" -} diff --git a/3-networks-svpc/modules/base_env/variables.tf b/3-networks-svpc/modules/base_env/variables.tf index 378bf1788..0c1b65b6d 100644 --- a/3-networks-svpc/modules/base_env/variables.tf +++ b/3-networks-svpc/modules/base_env/variables.tf @@ -35,11 +35,6 @@ variable "environment_code" { description = "A short form of the folder level resources (environment) within the Google Cloud organization (ex. d)." } -variable "access_context_manager_policy_id" { - type = number - description = "The id of the default Access Context Manager policy created in step `1-org`. Can be obtained by running `gcloud access-context-manager policies list --organization YOUR_ORGANIZATION_ID --format=\"value(name)\"`." -} - variable "default_region1" { type = string description = "First subnet region. The shared vpc modules only configures two regions." @@ -110,67 +105,6 @@ EOT default = {} } -variable "egress_policies" { - description = "A list of all [egress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#egress-rules-reference) to use in an enforced perimeter. Each list object has a `from` and `to` value that describes egress_from and egress_to.\n\nExample: `[{ from={ identities=[], identity_type=\"ID_TYPE\" }, to={ resources=[], operations={ \"SRV_NAME\"={ OP_TYPE=[] }}}}]`\n\nValid Values:\n`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`\n`SRV_NAME` = \"`*`\" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)\n`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions)" - type = list(object({ - from = any - to = any - })) - default = [] -} - -variable "egress_policies_dry_run" { - description = "A list of all [egress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#egress-rules-reference) to use in a dry-run perimeter. Each list object has a `from` and `to` value that describes egress_from and egress_to.\n\nExample: `[{ from={ identities=[], identity_type=\"ID_TYPE\" }, to={ resources=[], operations={ \"SRV_NAME\"={ OP_TYPE=[] }}}}]`\n\nValid Values:\n`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`\n`SRV_NAME` = \"`*`\" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)\n`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions)" - type = list(object({ - from = any - to = any - })) - default = [] -} - -variable "ingress_policies" { - description = "A list of all [ingress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#ingress-rules-reference) to use in an enforced perimeter. Each list object has a `from` and `to` value that describes ingress_from and ingress_to.\n\nExample: `[{ from={ sources={ resources=[], access_levels=[] }, identities=[], identity_type=\"ID_TYPE\" }, to={ resources=[], operations={ \"SRV_NAME\"={ OP_TYPE=[] }}}}]`\n\nValid Values:\n`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`\n`SRV_NAME` = \"`*`\" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)\n`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions)" - type = list(object({ - from = any - to = any - })) - default = [] -} - -variable "ingress_policies_dry_run" { - description = "A list of all [ingress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#ingress-rules-reference) to use in a dry-run perimeter. Each list object has a `from` and `to` value that describes ingress_from and ingress_to.\n\nExample: `[{ from={ sources={ resources=[], access_levels=[] }, identities=[], identity_type=\"ID_TYPE\" }, to={ resources=[], operations={ \"SRV_NAME\"={ OP_TYPE=[] }}}}]`\n\nValid Values:\n`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`\n`SRV_NAME` = \"`*`\" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)\n`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions)" - type = list(object({ - from = any - to = any - })) - default = [] -} - -variable "perimeter_additional_members" { - description = "The list of additional members to be added to the enforced perimeter access level members list. To be able to see the resources protected by the VPC Service Controls in the restricted perimeter, add your user in this list. Entries must be in the standard GCP form: `user:email@example.com` or `serviceAccount:my-service-account@example.com`." - type = list(string) - default = [] -} - -variable "perimeter_additional_members_dry_run" { - description = "The list of additional members to be added to the dry-run perimeter access level members list. To be able to see the resources protected by the VPC Service Controls in the restricted perimeter, add your user in this list. Entries must be in the standard GCP form: `user:email@example.com` or `serviceAccount:my-service-account@example.com`." - type = list(string) - default = [] -} - - -variable "custom_restricted_services" { - description = "List of custom services to be protected by the enforced VPC-SC perimeter. If empty, all supported services (https://cloud.google.com/vpc-service-controls/docs/supported-products) will be protected." - type = list(string) - default = [] -} - -variable "custom_restricted_services_dry_run" { - description = "List of custom services to be protected by the dry-run VPC-SC perimeter. If empty, all supported services (https://cloud.google.com/vpc-service-controls/docs/supported-products) will be protected." - type = list(string) - default = [] -} - variable "tfc_org_name" { description = "Name of the TFC organization" type = string diff --git a/3-networks-svpc/modules/shared_vpc/README.md b/3-networks-svpc/modules/shared_vpc/README.md index cfec1feae..76c8a30ee 100644 --- a/3-networks-svpc/modules/shared_vpc/README.md +++ b/3-networks-svpc/modules/shared_vpc/README.md @@ -3,7 +3,6 @@ | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| access\_context\_manager\_policy\_id | The id of the default Access Context Manager policy. Can be obtained by running `gcloud access-context-manager policies list --organization YOUR_ORGANIZATION_ID --format="value(name)"`. | `number` | n/a | yes | | bgp\_asn\_subnet | BGP ASN for Subnets cloud routers. | `number` | n/a | yes | | default\_region1 | First subnet region. The shared vpc modules only configures two regions. | `string` | n/a | yes | | default\_region2 | Second subnet region. The shared vpc modules only configures two regions. | `string` | n/a | yes | @@ -11,16 +10,9 @@ | dns\_enable\_logging | Toggle DNS logging for VPC DNS. | `bool` | `true` | no | | dns\_project\_id | Project ID for DNS Restricted Shared. | `string` | `""` | no | | domain | The DNS name of peering managed zone, for instance 'example.com.' | `string` | n/a | yes | -| egress\_policies | A list of all [egress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#egress-rules-reference) to use in an enforced perimeter. Each list object has a `from` and `to` value that describes egress\_from and egress\_to.

Example: `[{ from={ identities=[], identity_type="ID_TYPE" }, to={ resources=[], operations={ "SRV_NAME"={ OP_TYPE=[] }}}}]`

Valid Values:
`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`
`SRV_NAME` = "`*`" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)
`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) |
list(object({
from = any
to = any
}))
| `[]` | no | -| egress\_policies\_dry\_run | A list of all [egress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#egress-rules-reference) to use in a dry-run perimeter. Each list object has a `from` and `to` value that describes egress\_from and egress\_to.

Example: `[{ from={ identities=[], identity_type="ID_TYPE" }, to={ resources=[], operations={ "SRV_NAME"={ OP_TYPE=[] }}}}]`

Valid Values:
`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`
`SRV_NAME` = "`*`" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)
`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) |
list(object({
from = any
to = any
}))
| `[]` | no | | enable\_all\_vpc\_internal\_traffic | Enable firewall policy rule to allow internal traffic (ingress and egress). | `bool` | `false` | no | -| enforce\_vpcsc | Enable the enforced mode for VPC Service Controls. It is not recommended to enable VPC-SC on the first run deploying your foundation. Review [best practices for enabling VPC Service Controls](https://cloud.google.com/vpc-service-controls/docs/enable), then only enforce the perimeter after you have analyzed the access patterns in your dry-run perimeter and created the necessary exceptions for your use cases. | `bool` | `false` | no | | environment\_code | A short form of the folder level resources (environment) within the Google Cloud organization. | `string` | n/a | yes | | firewall\_enable\_logging | Toggle firewall logging for VPC Firewalls. | `bool` | `true` | no | -| ingress\_policies | A list of all [ingress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#ingress-rules-reference) to use in an enforced perimeter. Each list object has a `from` and `to` value that describes ingress\_from and ingress\_to.

Example: `[{ from={ sources={ resources=[], access_levels=[] }, identities=[], identity_type="ID_TYPE" }, to={ resources=[], operations={ "SRV_NAME"={ OP_TYPE=[] }}}}]`

Valid Values:
`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`
`SRV_NAME` = "`*`" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)
`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) |
list(object({
from = any
to = any
}))
| `[]` | no | -| ingress\_policies\_dry\_run | A list of all [ingress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#ingress-rules-reference) to use in a dry-run perimeter. Each list object has a `from` and `to` value that describes ingress\_from and ingress\_to.

Example: `[{ from={ sources={ resources=[], access_levels=[] }, identities=[], identity_type="ID_TYPE" }, to={ resources=[], operations={ "SRV_NAME"={ OP_TYPE=[] }}}}]`

Valid Values:
`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`
`SRV_NAME` = "`*`" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)
`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) |
list(object({
from = any
to = any
}))
| `[]` | no | -| members | An allowed list of members (users, service accounts) for an access level in an enforced perimeter. The signed-in identity originating the request must be a part of one of the provided members. If not specified, a request may come from any user (logged in/not logged in, etc.). Formats: user:{emailid}, serviceAccount:{emailid} | `list(string)` | n/a | yes | -| members\_dry\_run | An allowed list of members (users, service accounts) for an access level in a dry run perimeter. The signed-in identity originating the request must be a part of one of the provided members. If not specified, a request may come from any user (logged in/not logged in, etc.). Formats: user:{emailid}, serviceAccount:{emailid} | `list(string)` | n/a | yes | | nat\_bgp\_asn | BGP ASN for NAT cloud routes. If NAT is enabled this variable value must be a value in ranges [64512..65534] or [4200000000..4294967294]. | `number` | `64512` | no | | nat\_enabled | Toggle creation of NAT cloud router. | `bool` | `false` | no | | nat\_num\_addresses\_region1 | Number of external IPs to reserve for region 1 Cloud NAT. | `number` | `2` | no | @@ -29,8 +21,6 @@ | private\_service\_connect\_ip | Internal IP to be used as the private service connect endpoint. | `string` | n/a | yes | | project\_id | Project ID for Shared VPC. | `string` | n/a | yes | | project\_number | Project number for Shared VPC. | `number` | n/a | yes | -| restricted\_services | List of services to restrict in an enforced perimeter. | `list(string)` | n/a | yes | -| restricted\_services\_dry\_run | List of services to restrict in a dry-run perimeter. | `list(string)` | n/a | yes | | secondary\_ranges | Secondary ranges that will be used in some of the subnets | `map(list(object({ range_name = string, ip_cidr_range = string })))` | `{}` | no | | subnets | The list of subnets being created |
list(object({
subnet_name = string
subnet_ip = string
subnet_region = string
subnet_private_access = optional(string, "false")
subnet_private_ipv6_access = optional(string)
subnet_flow_logs = optional(string, "false")
subnet_flow_logs_interval = optional(string, "INTERVAL_5_SEC")
subnet_flow_logs_sampling = optional(string, "0.5")
subnet_flow_logs_metadata = optional(string, "INCLUDE_ALL_METADATA")
subnet_flow_logs_filter = optional(string, "true")
subnet_flow_logs_metadata_fields = optional(list(string), [])
description = optional(string)
purpose = optional(string)
role = optional(string)
stack_type = optional(string)
ipv6_access_type = optional(string)
}))
| `[]` | no | | target\_name\_server\_addresses | List of IPv4 address of target name servers for the forwarding zone configuration. See https://cloud.google.com/dns/docs/overview#dns-forwarding-zones for details on target name servers in the context of Cloud DNS forwarding zones. | `list(map(any))` | n/a | yes | @@ -40,16 +30,12 @@ | Name | Description | |------|-------------| -| access\_level\_name | Access context manager access level name for the enforced perimeter | -| access\_level\_name\_dry\_run | Access context manager access level name for the dry-run perimeter | -| enforce\_vpcsc | Enable the enforced mode for VPC Service Controls. It is not recommended to enable VPC-SC on the first run deploying your foundation. Review [best practices for enabling VPC Service Controls](https://cloud.google.com/vpc-service-controls/docs/enable), then only enforce the perimeter after you have analyzed the access patterns in your dry-run perimeter and created the necessary exceptions for your use cases. | | network\_name | The name of the VPC being created | | network\_self\_link | The URI of the VPC being created | | region1\_router1 | Router 1 for Region 1 | | region1\_router2 | Router 2 for Region 1 | | region2\_router1 | Router 1 for Region 2 | | region2\_router2 | Router 2 for Region 2 | -| service\_perimeter\_name | Access context manager service perimeter name for the enforced perimeter | | subnets\_ips | The IPs and CIDRs of the subnets being created | | subnets\_names | The names of the subnets being created | | subnets\_regions | The region where the subnets will be created | diff --git a/3-networks-svpc/modules/shared_vpc/outputs.tf b/3-networks-svpc/modules/shared_vpc/outputs.tf index 748ec4ca3..fd1622a7f 100644 --- a/3-networks-svpc/modules/shared_vpc/outputs.tf +++ b/3-networks-svpc/modules/shared_vpc/outputs.tf @@ -68,24 +68,3 @@ output "region2_router2" { value = module.region2_router2 description = "Router 2 for Region 2" } - -output "access_level_name" { - value = local.access_level_name - description = "Access context manager access level name for the enforced perimeter" -} - -output "access_level_name_dry_run" { - value = local.access_level_name_dry_run - description = "Access context manager access level name for the dry-run perimeter" -} - -output "enforce_vpcsc" { - value = var.enforce_vpcsc - description = "Enable the enforced mode for VPC Service Controls. It is not recommended to enable VPC-SC on the first run deploying your foundation. Review [best practices for enabling VPC Service Controls](https://cloud.google.com/vpc-service-controls/docs/enable), then only enforce the perimeter after you have analyzed the access patterns in your dry-run perimeter and created the necessary exceptions for your use cases." -} - -output "service_perimeter_name" { - value = local.perimeter_name - description = "Access context manager service perimeter name for the enforced perimeter" -} - diff --git a/3-networks-svpc/modules/shared_vpc/variables.tf b/3-networks-svpc/modules/shared_vpc/variables.tf index 6342467f2..a5f6a2b69 100644 --- a/3-networks-svpc/modules/shared_vpc/variables.tf +++ b/3-networks-svpc/modules/shared_vpc/variables.tf @@ -25,11 +25,6 @@ variable "target_name_server_addresses" { type = list(map(any)) } -variable "access_context_manager_policy_id" { - type = number - description = "The id of the default Access Context Manager policy. Can be obtained by running `gcloud access-context-manager policies list --organization YOUR_ORGANIZATION_ID --format=\"value(name)\"`." -} - variable "project_id" { type = string description = "Project ID for Shared VPC." @@ -153,71 +148,8 @@ variable "windows_activation_enabled" { default = false } -variable "members" { - type = list(string) - description = "An allowed list of members (users, service accounts) for an access level in an enforced perimeter. The signed-in identity originating the request must be a part of one of the provided members. If not specified, a request may come from any user (logged in/not logged in, etc.). Formats: user:{emailid}, serviceAccount:{emailid}" -} - -variable "members_dry_run" { - type = list(string) - description = "An allowed list of members (users, service accounts) for an access level in a dry run perimeter. The signed-in identity originating the request must be a part of one of the provided members. If not specified, a request may come from any user (logged in/not logged in, etc.). Formats: user:{emailid}, serviceAccount:{emailid}" -} - -variable "restricted_services" { - type = list(string) - description = "List of services to restrict in an enforced perimeter." -} - -variable "restricted_services_dry_run" { - type = list(string) - description = "List of services to restrict in a dry-run perimeter." -} - variable "enable_all_vpc_internal_traffic" { type = bool description = "Enable firewall policy rule to allow internal traffic (ingress and egress)." default = false } - -variable "enforce_vpcsc" { - description = "Enable the enforced mode for VPC Service Controls. It is not recommended to enable VPC-SC on the first run deploying your foundation. Review [best practices for enabling VPC Service Controls](https://cloud.google.com/vpc-service-controls/docs/enable), then only enforce the perimeter after you have analyzed the access patterns in your dry-run perimeter and created the necessary exceptions for your use cases." - type = bool - default = false -} - -variable "egress_policies" { - description = "A list of all [egress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#egress-rules-reference) to use in an enforced perimeter. Each list object has a `from` and `to` value that describes egress_from and egress_to.\n\nExample: `[{ from={ identities=[], identity_type=\"ID_TYPE\" }, to={ resources=[], operations={ \"SRV_NAME\"={ OP_TYPE=[] }}}}]`\n\nValid Values:\n`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`\n`SRV_NAME` = \"`*`\" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)\n`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions)" - type = list(object({ - from = any - to = any - })) - default = [] -} - -variable "egress_policies_dry_run" { - description = "A list of all [egress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#egress-rules-reference) to use in a dry-run perimeter. Each list object has a `from` and `to` value that describes egress_from and egress_to.\n\nExample: `[{ from={ identities=[], identity_type=\"ID_TYPE\" }, to={ resources=[], operations={ \"SRV_NAME\"={ OP_TYPE=[] }}}}]`\n\nValid Values:\n`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`\n`SRV_NAME` = \"`*`\" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)\n`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions)" - type = list(object({ - from = any - to = any - })) - default = [] -} - -variable "ingress_policies" { - description = "A list of all [ingress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#ingress-rules-reference) to use in an enforced perimeter. Each list object has a `from` and `to` value that describes ingress_from and ingress_to.\n\nExample: `[{ from={ sources={ resources=[], access_levels=[] }, identities=[], identity_type=\"ID_TYPE\" }, to={ resources=[], operations={ \"SRV_NAME\"={ OP_TYPE=[] }}}}]`\n\nValid Values:\n`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`\n`SRV_NAME` = \"`*`\" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)\n`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions)" - type = list(object({ - from = any - to = any - })) - default = [] -} - -variable "ingress_policies_dry_run" { - description = "A list of all [ingress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#ingress-rules-reference) to use in a dry-run perimeter. Each list object has a `from` and `to` value that describes ingress_from and ingress_to.\n\nExample: `[{ from={ sources={ resources=[], access_levels=[] }, identities=[], identity_type=\"ID_TYPE\" }, to={ resources=[], operations={ \"SRV_NAME\"={ OP_TYPE=[] }}}}]`\n\nValid Values:\n`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`\n`SRV_NAME` = \"`*`\" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)\n`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions)" - type = list(object({ - from = any - to = any - })) - default = [] -} - diff --git a/4-projects/README.md b/4-projects/README.md index 51e7af1e1..b9af1b3db 100644 --- a/4-projects/README.md +++ b/4-projects/README.md @@ -219,12 +219,93 @@ grep -rl 10.3.64.0 business_unit_2/ | xargs sed -i 's/10.3.64.0/10.4.64.0/g' git push origin nonproduction ``` -1. Before executing the next step, unset the `GOOGLE_IMPERSONATE_SERVICE_ACCOUNT` environment variable. +1. Unset the `GOOGLE_IMPERSONATE_SERVICE_ACCOUNT` environment variable. ```bash unset GOOGLE_IMPERSONATE_SERVICE_ACCOUNT ``` +1. Before move to the next step, configure directional policies for your perimeter. + +1. Navigate to `gcp-org`. +1. Use `terraform output` to get the APP Infra Pipeline cloud build project id and project number. + + ```bash + cd ../gcp-org/ + + export cloudbuild_project_number=$(terraform -chdir="../gcp-projects/business_unit_1/shared/" output -raw cloudbuild_project_number) + echo "cloud build project number = $cloudbuild_project_number" + export cloudbuild_project_id=$(terraform -chdir="../gcp-projects/business_unit_1/shared/" output -raw cloudbuild_project_id) + echo "cloud build project id = $cloudbuild_project_id" + sed -i'' -e "s/PRJ_APP_INFRA_PIPELINE_NUMBER/${cloudbuild_project_number}/" /envs/shared/service_control.tf + sed -i'' -e "s/PRJ_APP_INFRA_ID/${cloudbuild_project_id}/" /envs/shared/service_control.tf + ``` + +1. Use `terraform output` to get the Bucket used for storing terraform state for stage 4-projects foundations pipelines in seed project. +1. Use `gsutil cat` to get the project numbers of the SVPC and Peering projects in each environment (production, nonproduction, and development) for configuring the directional app infra policies. + + ```bash + export projects_gcs_bucket_tfstate=$(terraform -chdir="../terraform-example-foundation/0-bootstrap/" output -raw projects_gcs_bucket_tfstate) + echo "projects_gcs_bucket_tfstate = ${projects_gcs_bucket_tfstate}" + + export peering_project_number_dev=$(gsutil cat gs://${projects_gcs_bucket_tfstate}/terraform/projects/business_unit_1/development/default.tfstate \ + | jq -r '.outputs.peering_project_number.value') + echo "peering_project_number_dev = ${peering_project_number_dev}" + sed -i'' -e "s/PRJS_DEV_SAMPLE_PEERING_NUMBER/${peering_project_number_dev}/" envs/shared/service_control.tf + + export peering_project_number_prod=$(gsutil cat gs://${projects_gcs_bucket_tfstate}/terraform/projects/business_unit_1/production/default.tfstate \ + | jq -r '.outputs.peering_project_number.value') + echo "peering_project_number_prod = ${peering_project_number_prod}" + sed -i'' -e "s/PRJS_PROD_SAMPLE_PEERING_NUMBER/${peering_project_number_prod}/" envs/shared/service_control.tf + + export peering_project_number_nonprod=$(gsutil cat gs://${projects_gcs_bucket_tfstate}/terraform/projects/business_unit_1/nonproduction/default.tfstate \ + | jq -r '.outputs.peering_project_number.value') + echo "peering_project_number_nonprod = ${peering_project_number_nonprod}" + sed -i'' -e "s/PRJS_NONPROD_SAMPLE_PEERING_NUMBER/${peering_project_number_nonprod}/" envs/shared/service_control.tf + + export shared_vpc_project_number_dev=$(gsutil cat gs://${projects_gcs_bucket_tfstate}/terraform/projects/business_unit_1/development/default.tfstate \ + | jq -r '.outputs.shared_vpc_project_number.value') + echo "shared_vpc_project_number_dev = ${shared_vpc_project_number_dev}" + sed -i'' -e "s/PRJS_DEV_SAMPLE_SVPC_NUMBER/${shared_vpc_project_number_dev}/" /envs/shared/service_control.tf + + export shared_vpc_project_number_prod=$(gsutil cat gs://${projects_gcs_bucket_tfstate}/terraform/projects/business_unit_1/production/default.tfstate \ + | jq -r '.outputs.shared_vpc_project_number.value') + echo "shared_vpc_project_number_prod = ${shared_vpc_project_number_prod}" + sed -i'' -e "s/PRJS_PROD_SAMPLE_SVPC_NUMBER/${shared_vpc_project_number_prod}/" /envs/shared/service_control.tf + + export shared_vpc_project_number_nonprod=$(gsutil cat gs://${projects_gcs_bucket_tfstate}/terraform/projects/business_unit_1/nonproduction/default.tfstate \ + | jq -r '.outputs.shared_vpc_project_number.value') + echo "shared_vpc_project_number_nonprod = ${shared_vpc_project_number_nonprod}" + sed -i'' -e "s/PRJS_NONPROD_SAMPLE_SVPC_NUMBER/${shared_vpc_project_number_nonprod}/" /envs/shared/service_control.tf + ``` + +1. If you are deploying with VPC Service Controls in dry run mode, update the `required_egress_rule_app_infra_dry_run` and `required_ingress_rule_app_infra_dry_run` variables to true, if you are deploying with VPC Service Controls in enforced mode, update the `required_egress_rule_app_infra` and `required_ingress_rule_app_infra` variables to true in [service_control.tf](gcp-org/envs/shared/service_control.tf) file. + + ```bash + export enforce_vpcsc=$(terraform -chdir="envs/shared/" output -raw enforce_vpcsc); \ + echo "enforce_vpcsc" = $enforce_vpcsc + if [[ "$enforce_vpcsc" == "false" ]]; then \ + sed -i -E '/^[[:space:]]*\/\/required_ingress_rules_app_infra_dry_run[[:space:]]*=/ s|^[[:space:]]*//||' envs/shared/terraform.tfvars; \ + else \ + sed -i -E '/^[[:space:]]*\/\/required_ingress_rules_app_infra[[:space:]]*=/ s|^[[:space:]]*//||' envs/shared/terraform.tfvars; \ + fi + if [[ "$enforce_vpcsc" == "false" ]]; then \ + sed -i -E '/^[[:space:]]*\/\/required_egress_rules_app_infra_dry_run[[:space:]]*=/ s|^[[:space:]]*//||' envs/shared/terraform.tfvars; \ + else \ + sed -i -E '/^[[:space:]]*\/\/required_egress_rules_app_infra[[:space:]]*=/ s|^[[:space:]]*//||' envs/shared/terraform.tfvars; \ + fi + ``` + +1. Commit and push the changes. + + ```bash + git add . + git commit -m "Add infra pipeline directional policies" + git push + + cd .. + ``` + 1. You can now move to the instructions in the [5-app-infra](../5-app-infra/README.md) step. ### Deploying with Jenkins @@ -274,7 +355,7 @@ See `0-bootstrap` [README-GitHub.md](../0-bootstrap/README-GitHub.md#deploying-s Use `terraform output` to get the remote state bucket (the backend bucket used by previous steps) value from `gcp-bootstrap` output. ```bash - export remote_state_bucket=$(terraform -chdir="../gcp-bootstrap/" output -raw gcs_bucket_tfstate) + export remote_state_bucket=$(terraform -chdir="../gcp-bootstrap/envs/shared/" output -raw gcs_bucket_tfstate) echo "remote_state_bucket = ${remote_state_bucket}" sed -i'' -e "s/REMOTE_STATE_BUCKET/${remote_state_bucket}/" ./common.auto.tfvars @@ -287,10 +368,10 @@ To use the `validate` option of the `tf-wrapper.sh` script, please follow the [i 1. Use `terraform output` to get the Seed project ID and the organization step Terraform service account from gcp-bootstrap output. An environment variable `GOOGLE_IMPERSONATE_SERVICE_ACCOUNT` will be set using the Terraform Service Account to enable impersonation. ```bash - export SEED_PROJECT_ID=$(terraform -chdir="../gcp-bootstrap/" output -raw seed_project_id) + export SEED_PROJECT_ID=$(terraform -chdir="../gcp-bootstrap/envs/shared/" output -raw seed_project_id) echo ${SEED_PROJECT_ID} - export GOOGLE_IMPERSONATE_SERVICE_ACCOUNT=$(terraform -chdir="../gcp-bootstrap/" output -raw projects_step_terraform_service_account_email) + export GOOGLE_IMPERSONATE_SERVICE_ACCOUNT=$(terraform -chdir="../gcp-bootstrap/envs/shared/" output -raw projects_step_terraform_service_account_email) echo ${GOOGLE_IMPERSONATE_SERVICE_ACCOUNT} ``` @@ -377,7 +458,7 @@ grep -rl 10.3.64.0 business_unit_2/ | xargs sed -i 's/10.3.64.0/10.4.64.0/g' git commit -m "Initial nonproduction commit." ``` -1. Checkout shared `production`. Run `init` and `plan` and review output for environment development. +1. Checkout shared `production`. Run `init` and `plan` and review output for production development. ```bash git checkout production @@ -403,8 +484,113 @@ grep -rl 10.3.64.0 business_unit_2/ | xargs sed -i 's/10.3.64.0/10.4.64.0/g' If you received any errors or made any changes to the Terraform config or any `.tfvars`, you must re-run `./tf-wrapper.sh plan ` before run `./tf-wrapper.sh apply `. -Before executing the next stages, unset the `GOOGLE_IMPERSONATE_SERVICE_ACCOUNT` environment variable. +1. Unset the `GOOGLE_IMPERSONATE_SERVICE_ACCOUNT` environment variable. -```bash -unset GOOGLE_IMPERSONATE_SERVICE_ACCOUNT -``` + ```bash + unset GOOGLE_IMPERSONATE_SERVICE_ACCOUNT + ``` + +1. Before move to the next step, configure directional policies for your perimeter. + +1. Use `terraform output` to get the Seed project ID and the organization step Terraform service account from gcp-bootstrap output. An environment variable `GOOGLE_IMPERSONATE_SERVICE_ACCOUNT` will be set using the Terraform Service Account to enable impersonation. + + ```bash + export SEED_PROJECT_ID=$(terraform -chdir="../gcp-bootstrap/envs/shared/" output -raw seed_project_id) + echo ${SEED_PROJECT_ID} + + export GOOGLE_IMPERSONATE_SERVICE_ACCOUNT=$(terraform -chdir="../gcp-bootstrap/envs/shared/" output -raw organization_step_terraform_service_account_email) + echo ${GOOGLE_IMPERSONATE_SERVICE_ACCOUNT} + ``` + +1. Before move to the next step, configure directional policies for your perimeter. + +1. Navigate to `gcp-org`. +1. Use `terraform output` to get the APP Infra Pipeline cloud build project id and project number. + + ```bash + cd ../gcp-org/ + + export cloudbuild_project_number=$(terraform -chdir="../gcp-projects/business_unit_1/shared/" output -raw cloudbuild_project_number) + echo "cloud build project number = $cloudbuild_project_number" + export cloudbuild_project_id=$(terraform -chdir="../gcp-projects/business_unit_1/shared/" output -raw cloudbuild_project_id) + echo "cloud build project id = $cloudbuild_project_id" + sed -i'' -e "s/PRJ_APP_INFRA_PIPELINE_NUMBER/${cloudbuild_project_number}/" /envs/shared/service_control.tf + sed -i'' -e "s/PRJ_APP_INFRA_ID/${cloudbuild_project_id}/" /envs/shared/service_control.tf + ``` + +1. Use `terraform output` to get the Bucket used for storing terraform state for stage 4-projects foundations pipelines in seed project. +1. Use `gsutil cat` to get the project numbers of the SVPC and Peering projects in each environment (production, nonproduction, and development) for configuring the directional app infra policies. + + ```bash + export projects_gcs_bucket_tfstate=$(terraform -chdir="../terraform-example-foundation/0-bootstrap/" output -raw projects_gcs_bucket_tfstate) + echo "projects_gcs_bucket_tfstate = ${projects_gcs_bucket_tfstate}" + + export peering_project_number_dev=$(gsutil cat gs://${projects_gcs_bucket_tfstate}/terraform/projects/business_unit_1/development/default.tfstate \ + | jq -r '.outputs.peering_project_number.value') + echo "peering_project_number_dev = ${peering_project_number_dev}" + sed -i'' -e "s/PRJS_DEV_SAMPLE_PEERING_NUMBER/${peering_project_number_dev}/" envs/shared/service_control.tf + + export peering_project_number_prod=$(gsutil cat gs://${projects_gcs_bucket_tfstate}/terraform/projects/business_unit_1/production/default.tfstate \ + | jq -r '.outputs.peering_project_number.value') + echo "peering_project_number_prod = ${peering_project_number_prod}" + sed -i'' -e "s/PRJS_PROD_SAMPLE_PEERING_NUMBER/${peering_project_number_prod}/" envs/shared/service_control.tf + + export peering_project_number_nonprod=$(gsutil cat gs://${projects_gcs_bucket_tfstate}/terraform/projects/business_unit_1/nonproduction/default.tfstate \ + | jq -r '.outputs.peering_project_number.value') + echo "peering_project_number_nonprod = ${peering_project_number_nonprod}" + sed -i'' -e "s/PRJS_NONPROD_SAMPLE_PEERING_NUMBER/${peering_project_number_nonprod}/" envs/shared/service_control.tf + + export shared_vpc_project_number_dev=$(gsutil cat gs://${projects_gcs_bucket_tfstate}/terraform/projects/business_unit_1/development/default.tfstate \ + | jq -r '.outputs.shared_vpc_project_number.value') + echo "shared_vpc_project_number_dev = ${shared_vpc_project_number_dev}" + sed -i'' -e "s/PRJS_DEV_SAMPLE_SVPC_NUMBER/${shared_vpc_project_number_dev}/" /envs/shared/service_control.tf + + export shared_vpc_project_number_prod=$(gsutil cat gs://${projects_gcs_bucket_tfstate}/terraform/projects/business_unit_1/production/default.tfstate \ + | jq -r '.outputs.shared_vpc_project_number.value') + echo "shared_vpc_project_number_prod = ${shared_vpc_project_number_prod}" + sed -i'' -e "s/PRJS_PROD_SAMPLE_SVPC_NUMBER/${shared_vpc_project_number_prod}/" /envs/shared/service_control.tf + + export shared_vpc_project_number_nonprod=$(gsutil cat gs://${projects_gcs_bucket_tfstate}/terraform/projects/business_unit_1/nonproduction/default.tfstate \ + | jq -r '.outputs.shared_vpc_project_number.value') + echo "shared_vpc_project_number_nonprod = ${shared_vpc_project_number_nonprod}" + sed -i'' -e "s/PRJS_NONPROD_SAMPLE_SVPC_NUMBER/${shared_vpc_project_number_nonprod}/" /envs/shared/service_control.tf + ``` + +1. If you are deploying with VPC Service Controls in dry run mode, update the `required_egress_rule_app_infra_dry_run` and `required_ingress_rule_app_infra_dry_run` variables to true, if you are deploying with VPC Service Controls in enforced mode, update the `required_egress_rule_app_infra` and `required_ingress_rule_app_infra` variables to true in [service_control.tf](gcp-org/envs/shared/service_control.tf) file. + + ```bash + export enforce_vpcsc=$(terraform -chdir="envs/shared/" output -raw enforce_vpcsc); \ + echo "enforce_vpcsc" = $enforce_vpcsc + if [[ "$enforce_vpcsc" == "false" ]]; then \ + sed -i -E '/^[[:space:]]*\/\/required_ingress_rules_app_infra_dry_run[[:space:]]*=/ s|^[[:space:]]*//||' envs/shared/terraform.tfvars; \ + else \ + sed -i -E '/^[[:space:]]*\/\/required_ingress_rules_app_infra[[:space:]]*=/ s|^[[:space:]]*//||' envs/shared/terraform.tfvars; \ + fi + if [[ "$enforce_vpcsc" == "false" ]]; then \ + sed -i -E '/^[[:space:]]*\/\/required_egress_rules_app_infra_dry_run[[:space:]]*=/ s|^[[:space:]]*//||' envs/shared/terraform.tfvars; \ + else \ + sed -i -E '/^[[:space:]]*\/\/required_egress_rules_app_infra[[:space:]]*=/ s|^[[:space:]]*//||' envs/shared/terraform.tfvars; \ + fi + ``` + +1. Run `plan` and `apply` and output for production development. Commit and save the changes. + + ```bash + ./tf-wrapper plan production + ./tf-wrapper apply production + git commit -m "Add infra pipeline directional policies" + + cd .. + ``` + +1. Unset the `GOOGLE_IMPERSONATE_SERVICE_ACCOUNT` environment variable. + + ```bash + unset GOOGLE_IMPERSONATE_SERVICE_ACCOUNT + ``` + +1. You can now move to the instructions in the [5-app-infra](../5-app-infra/README.md) step. + +## Warning + +If you previously enabled the dry-run variables `required_ingress_rules_app_infra_dry_run` and `required_egress_rules_app_infra_dry_run`, you must **disable both of them before running the 4-projects step in enforced mode**. Once enforced mode is activated and the **4-projects** step is executed, the projects created in that step are removed from the dry-run perimeter. Keeping these dry-run variables active creates conflicts, because they define rules for projects that will no longer be part of the dry-run perimeter after this transition. diff --git a/4-projects/business_unit_1/development/README.md b/4-projects/business_unit_1/development/README.md index cad2aa934..de526fc9e 100644 --- a/4-projects/business_unit_1/development/README.md +++ b/4-projects/business_unit_1/development/README.md @@ -24,12 +24,14 @@ | confidential\_space\_workload\_sa | Workload Service Account for confidential space from base\_env | | default\_region | The default region for the project. | | floating\_project | Project sample floating project. | +| floating\_project\_number | Project number sample floating project. | | iap\_firewall\_tags | The security tags created for IAP (SSH and RDP) firewall rules and to be used on the VM created on step 5-app-infra on the peering network project. | | keyring | The name of the keyring. | | keys | List of created key names. | | peering\_complete | Output to be used as a module dependency. | | peering\_network | Peer network peering resource. | | peering\_project | Project sample peering project id. | +| peering\_project\_number | Project sample shared vpc project. | | peering\_subnetwork\_self\_link | The subnetwork self link of the peering network. | | restricted\_enabled\_apis | Activated APIs. | | shared\_vpc\_project | Project sample shared vpc project id. | diff --git a/4-projects/business_unit_1/development/outputs.tf b/4-projects/business_unit_1/development/outputs.tf index e81e10580..2d7b525de 100644 --- a/4-projects/business_unit_1/development/outputs.tf +++ b/4-projects/business_unit_1/development/outputs.tf @@ -19,11 +19,21 @@ output "floating_project" { value = module.env.floating_project } +output "floating_project_number" { + description = "Project number sample floating project." + value = module.env.floating_project_number +} + output "peering_project" { description = "Project sample peering project id." value = module.env.peering_project } +output "peering_project_number" { + description = "Project sample shared vpc project." + value = module.env.peering_project_number +} + output "peering_network" { description = "Peer network peering resource." value = module.env.peering_network diff --git a/4-projects/business_unit_1/nonproduction/README.md b/4-projects/business_unit_1/nonproduction/README.md index 802307a77..abb3f8d01 100644 --- a/4-projects/business_unit_1/nonproduction/README.md +++ b/4-projects/business_unit_1/nonproduction/README.md @@ -24,12 +24,14 @@ | confidential\_space\_workload\_sa | Workload Service Account for confidential space from base\_env | | default\_region | The default region for the project. | | floating\_project | Project sample floating project. | +| floating\_project\_number | Project number sample floating project. | | iap\_firewall\_tags | The security tags created for IAP (SSH and RDP) firewall rules and to be used on the VM created on step 5-app-infra on the peering network project. | | keyring | The name of the keyring. | | keys | List of created key names. | | peering\_complete | Output to be used as a module dependency. | | peering\_network | Peer network peering resource. | | peering\_project | Project sample peering project id. | +| peering\_project\_number | Project sample shared vpc project. | | peering\_subnetwork\_self\_link | The subnetwork self link of the peering network. | | restricted\_enabled\_apis | Activated APIs. | | shared\_vpc\_project | Project sample project id. | diff --git a/4-projects/business_unit_1/nonproduction/outputs.tf b/4-projects/business_unit_1/nonproduction/outputs.tf index ac13c9920..c304d6525 100644 --- a/4-projects/business_unit_1/nonproduction/outputs.tf +++ b/4-projects/business_unit_1/nonproduction/outputs.tf @@ -19,11 +19,21 @@ output "floating_project" { value = module.env.floating_project } +output "floating_project_number" { + description = "Project number sample floating project." + value = module.env.floating_project_number +} + output "peering_project" { description = "Project sample peering project id." value = module.env.peering_project } +output "peering_project_number" { + description = "Project sample shared vpc project." + value = module.env.peering_project_number +} + output "peering_network" { description = "Peer network peering resource." value = module.env.peering_network diff --git a/4-projects/business_unit_1/production/README.md b/4-projects/business_unit_1/production/README.md index 5dbe98f81..0d1e6f066 100644 --- a/4-projects/business_unit_1/production/README.md +++ b/4-projects/business_unit_1/production/README.md @@ -24,12 +24,14 @@ | confidential\_space\_workload\_sa | Workload Service Account for confidential space from base\_env | | default\_region | The default region for the project. | | floating\_project | Project sample floating project. | +| floating\_project\_number | Project number sample floating project. | | iap\_firewall\_tags | The security tags created for IAP (SSH and RDP) firewall rules and to be used on the VM created on step 5-app-infra on the peering network project. | | keyring | The name of the keyring. | | keys | List of created key names. | | peering\_complete | Output to be used as a module dependency. | | peering\_network | Peer network peering resource. | | peering\_project | Project sample peering project id. | +| peering\_project\_number | Project sample shared vpc project. | | peering\_subnetwork\_self\_link | The subnetwork self link of the peering network. | | restricted\_enabled\_apis | Activated APIs. | | shared\_vpc\_project | Project sample shared vpc project id. | diff --git a/4-projects/business_unit_1/production/outputs.tf b/4-projects/business_unit_1/production/outputs.tf index 99b461d78..2a1430543 100644 --- a/4-projects/business_unit_1/production/outputs.tf +++ b/4-projects/business_unit_1/production/outputs.tf @@ -19,11 +19,21 @@ output "floating_project" { value = module.env.floating_project } +output "floating_project_number" { + description = "Project number sample floating project." + value = module.env.floating_project_number +} + output "peering_project" { description = "Project sample peering project id." value = module.env.peering_project } +output "peering_project_number" { + description = "Project sample shared vpc project." + value = module.env.peering_project_number +} + output "peering_network" { description = "Peer network peering resource." value = module.env.peering_network diff --git a/4-projects/business_unit_1/shared/README.md b/4-projects/business_unit_1/shared/README.md index 00cf1c983..d0ab9e0fb 100644 --- a/4-projects/business_unit_1/shared/README.md +++ b/4-projects/business_unit_1/shared/README.md @@ -17,7 +17,8 @@ | artifact\_buckets | GCS Buckets to store Cloud Build Artifacts | | artifact\_registry\_repository\_id | Artifact Registry ID. | | bootstrap\_cloudbuild\_project\_id | Cloudbuild project ID. | -| cloudbuild\_project\_id | n/a | +| cloudbuild\_project\_id | APP Infra cloudbuild project id. | +| cloudbuild\_project\_number | APP Infra cloudbuild project number. | | default\_region | Default region to create resources where applicable. | | enable\_cloudbuild\_deploy | Enable infra deployment using Cloud Build. | | image\_name | Image path used by confidential space instance. | diff --git a/4-projects/business_unit_1/shared/example_infra_pipeline.tf b/4-projects/business_unit_1/shared/example_infra_pipeline.tf index 0e4848598..3f5aec2e3 100644 --- a/4-projects/business_unit_1/shared/example_infra_pipeline.tf +++ b/4-projects/business_unit_1/shared/example_infra_pipeline.tf @@ -84,6 +84,11 @@ module "app_infra_cloudbuild_project" { project_deletion_policy = var.project_deletion_policy + vpc_service_control_attach_enabled = local.enforce_vpcsc ? "true" : "false" + vpc_service_control_attach_dry_run = !local.enforce_vpcsc ? "true" : "false" + vpc_service_control_perimeter_name = "accessPolicies/${local.access_context_manager_policy_id}/servicePerimeters/${local.perimeter_name}" + vpc_service_control_sleep_duration = "60s" + activate_apis = [ "cloudbuild.googleapis.com", "sourcerepo.googleapis.com", diff --git a/4-projects/business_unit_1/shared/outputs.tf b/4-projects/business_unit_1/shared/outputs.tf index 0cfc52f4f..c0a8827f5 100644 --- a/4-projects/business_unit_1/shared/outputs.tf +++ b/4-projects/business_unit_1/shared/outputs.tf @@ -20,7 +20,13 @@ output "default_region" { } output "cloudbuild_project_id" { - value = try(module.app_infra_cloudbuild_project[0].project_id, "") + description = "APP Infra cloudbuild project id." + value = try(module.app_infra_cloudbuild_project[0].project_id, "") +} + +output "cloudbuild_project_number" { + description = "APP Infra cloudbuild project number." + value = module.app_infra_cloudbuild_project[0].project_number } output "terraform_service_accounts" { diff --git a/4-projects/business_unit_1/shared/remote.tf b/4-projects/business_unit_1/shared/remote.tf index 7836b6649..541e6b0b2 100644 --- a/4-projects/business_unit_1/shared/remote.tf +++ b/4-projects/business_unit_1/shared/remote.tf @@ -27,6 +27,9 @@ locals { cloud_build_private_worker_pool_id = try(data.terraform_remote_state.bootstrap.outputs.cloud_build_private_worker_pool_id, "") cloud_builder_artifact_repo = try(data.terraform_remote_state.bootstrap.outputs.cloud_builder_artifact_repo, "") enable_cloudbuild_deploy = local.cloud_builder_artifact_repo != "" + perimeter_name = data.terraform_remote_state.org.outputs.service_perimeter_name + enforce_vpcsc = data.terraform_remote_state.org.outputs.enforce_vpcsc + access_context_manager_policy_id = data.terraform_remote_state.org.outputs.access_context_manager_policy_id cloudbuild_project_id = data.terraform_remote_state.bootstrap.outputs.cloudbuild_project_id projects_terraform_sa = data.terraform_remote_state.bootstrap.outputs.projects_step_terraform_service_account_email } diff --git a/4-projects/modules/base_env/README.md b/4-projects/modules/base_env/README.md index 74645b0b9..317b89247 100644 --- a/4-projects/modules/base_env/README.md +++ b/4-projects/modules/base_env/README.md @@ -41,12 +41,14 @@ | confidential\_space\_project\_number | Confidential Space project number. | | confidential\_space\_workload\_sa | Workload Service Account for confidential space | | floating\_project | Project sample floating project. | +| floating\_project\_number | Project number sample floating project. | | iap\_firewall\_tags | The security tags created for IAP (SSH and RDP) firewall rules and to be used on the VM created on step 5-app-infra on the peering network project. | | keyring | The name of the keyring. | | keys | List of created key names. | | peering\_complete | Output to be used as a module dependency. | | peering\_network | Peer network peering resource. | | peering\_project | Project sample peering project id. | +| peering\_project\_number | Project sample peering project number. | | peering\_subnetwork\_self\_link | The subnetwork self link of the peering network. | | restricted\_enabled\_apis | Activated APIs. | | shared\_vpc\_project | Project sample restricted project id. | diff --git a/4-projects/modules/base_env/example_floating_project.tf b/4-projects/modules/base_env/example_floating_project.tf index e8bbe7406..db877762c 100644 --- a/4-projects/modules/base_env/example_floating_project.tf +++ b/4-projects/modules/base_env/example_floating_project.tf @@ -26,6 +26,11 @@ module "floating_project" { project_deletion_policy = var.project_deletion_policy + vpc_service_control_attach_enabled = local.enforce_vpcsc ? "true" : "false" + vpc_service_control_attach_dry_run = !local.enforce_vpcsc ? "true" : "false" + vpc_service_control_perimeter_name = "accessPolicies/${local.access_context_manager_policy_id}/servicePerimeters/${local.perimeter_name}" + vpc_service_control_sleep_duration = "60s" + # Metadata project_suffix = "sample-floating" application_name = "${var.business_code}-sample-application" diff --git a/4-projects/modules/base_env/example_peering_project.tf b/4-projects/modules/base_env/example_peering_project.tf index 6a2a36f7c..7f40781af 100644 --- a/4-projects/modules/base_env/example_peering_project.tf +++ b/4-projects/modules/base_env/example_peering_project.tf @@ -61,6 +61,11 @@ module "peering_project" { ] } + vpc_service_control_attach_enabled = local.enforce_vpcsc ? "true" : "false" + vpc_service_control_attach_dry_run = !local.enforce_vpcsc ? "true" : "false" + vpc_service_control_perimeter_name = "accessPolicies/${local.access_context_manager_policy_id}/servicePerimeters/${local.perimeter_name}" + vpc_service_control_sleep_duration = "60s" + activate_apis = [ "dns.googleapis.com" ] diff --git a/4-projects/modules/base_env/example_shared_vpc_project.tf b/4-projects/modules/base_env/example_shared_vpc_project.tf index 0f0b52784..139d9c9f5 100644 --- a/4-projects/modules/base_env/example_shared_vpc_project.tf +++ b/4-projects/modules/base_env/example_shared_vpc_project.tf @@ -39,7 +39,8 @@ module "shared_vpc_project" { ] } - activate_apis = ["accesscontextmanager.googleapis.com"] + activate_apis = ["accesscontextmanager.googleapis.com"] + vpc_service_control_attach_enabled = local.enforce_vpcsc ? "true" : "false" vpc_service_control_attach_dry_run = !local.enforce_vpcsc ? "true" : "false" vpc_service_control_perimeter_name = "accessPolicies/${local.access_context_manager_policy_id}/servicePerimeters/${local.perimeter_name}" diff --git a/4-projects/modules/base_env/outputs.tf b/4-projects/modules/base_env/outputs.tf index 5e3ac7c95..4a19f3945 100644 --- a/4-projects/modules/base_env/outputs.tf +++ b/4-projects/modules/base_env/outputs.tf @@ -19,11 +19,21 @@ output "floating_project" { value = module.floating_project.project_id } +output "floating_project_number" { + description = "Project number sample floating project." + value = module.floating_project.project_number +} + output "peering_project" { description = "Project sample peering project id." value = module.peering_project.project_id } +output "peering_project_number" { + description = "Project sample peering project number." + value = module.peering_project.project_number +} + output "peering_network" { description = "Peer network peering resource." value = module.peering.peer_network_peering diff --git a/4-projects/modules/base_env/remote.tf b/4-projects/modules/base_env/remote.tf index 869c39275..9fd9ed986 100644 --- a/4-projects/modules/base_env/remote.tf +++ b/4-projects/modules/base_env/remote.tf @@ -19,12 +19,12 @@ locals { billing_account = data.terraform_remote_state.bootstrap.outputs.common_config.billing_account project_prefix = data.terraform_remote_state.bootstrap.outputs.common_config.project_prefix projects_backend_bucket = data.terraform_remote_state.bootstrap.outputs.projects_gcs_bucket_tfstate - perimeter_name = data.terraform_remote_state.network_env.outputs.service_perimeter_name network_self_link = data.terraform_remote_state.network_env.outputs.network_self_link shared_vpc_host_project_id = data.terraform_remote_state.network_env.outputs.shared_vpc_host_project_id subnets_self_links = data.terraform_remote_state.network_env.outputs.subnets_self_links - access_context_manager_policy_id = data.terraform_remote_state.network_env.outputs.access_context_manager_policy_id - enforce_vpcsc = data.terraform_remote_state.network_env.outputs.enforce_vpcsc + access_context_manager_policy_id = data.terraform_remote_state.org_env.outputs.access_context_manager_policy_id + enforce_vpcsc = data.terraform_remote_state.org_env.outputs.enforce_vpcsc + perimeter_name = data.terraform_remote_state.org_env.outputs.service_perimeter_name env_folder_name = data.terraform_remote_state.environments_env.outputs.env_folder app_infra_pipeline_service_accounts = data.terraform_remote_state.business_unit_shared.outputs.terraform_service_accounts enable_cloudbuild_deploy = data.terraform_remote_state.business_unit_shared.outputs.enable_cloudbuild_deploy @@ -43,6 +43,15 @@ data "terraform_remote_state" "bootstrap" { } } +data "terraform_remote_state" "org_env" { + backend = "gcs" + + config = { + bucket = var.remote_state_bucket + prefix = "terraform/org/state" + } +} + data "terraform_remote_state" "network_env" { backend = "gcs" diff --git a/4-projects/modules/infra_pipelines/README.md b/4-projects/modules/infra_pipelines/README.md index 3738b0a74..5d1ce0a8a 100644 --- a/4-projects/modules/infra_pipelines/README.md +++ b/4-projects/modules/infra_pipelines/README.md @@ -13,8 +13,6 @@ | private\_worker\_pool\_id | ID of the Cloud Build private worker pool. | `string` | n/a | yes | | remote\_tfstate\_bucket | Bucket with remote state data to be used by the pipeline. | `string` | n/a | yes | | terraform\_docker\_tag\_version | TAG version of the terraform docker image. | `string` | `"v1"` | no | -| vpc\_service\_control\_attach\_dry\_run | Whether the project will be attached to a VPC Service Control Perimeter with an explicit dry run spec flag, which may use different values for the dry run perimeter compared to the ENFORCED perimeter. | `bool` | `false` | no | -| vpc\_service\_control\_attach\_enabled | Whether the project will be attached to a VPC Service Control Perimeter in ENFORCED MODE. | `bool` | `false` | no | ## Outputs diff --git a/4-projects/modules/infra_pipelines/variables.tf b/4-projects/modules/infra_pipelines/variables.tf index 35eef8e23..63492db1e 100644 --- a/4-projects/modules/infra_pipelines/variables.tf +++ b/4-projects/modules/infra_pipelines/variables.tf @@ -66,17 +66,3 @@ variable "bucket_prefix" { type = string default = "bkt" } - -variable "vpc_service_control_attach_enabled" { - description = "Whether the project will be attached to a VPC Service Control Perimeter in ENFORCED MODE." - type = bool - default = false -} - - -variable "vpc_service_control_attach_dry_run" { - description = "Whether the project will be attached to a VPC Service Control Perimeter with an explicit dry run spec flag, which may use different values for the dry run perimeter compared to the ENFORCED perimeter." - type = bool - default = false -} - diff --git a/docs/TROUBLESHOOTING.md b/docs/TROUBLESHOOTING.md index f73cd83e9..47d14dd9d 100644 --- a/docs/TROUBLESHOOTING.md +++ b/docs/TROUBLESHOOTING.md @@ -12,23 +12,34 @@ See [GLOSSARY.md](./GLOSSARY.md). - [Caller does not have permission in the Organization](#caller-does-not-have-permission-in-the-organization) - [Billing quota exceeded](#billing-quota-exceeded) - [Terraform Error acquiring the state lock](#terraform-error-acquiring-the-state-lock) +- [VPC Service Controls](#vpc-service-controls) - - - ## Common issues -- [Project quota exceeded](#project-quota-exceeded) -- [Default branch setting](#default-branch-setting) -- [Terraform State Snapshot lock](#terraform-state-snapshot-lock) -- [Application authenticated using end user credentials](#application-authenticated-using-end-user-credentials) -- [Cannot assign requested address error in Cloud Shell](#cannot-assign-requested-address-error-in-cloud-shell) -- [Error: Unsupported attribute](#error-unsupported-attribute) -- [Error: Error adding network peering](#error-error-adding-network-peering) -- [Error: Terraform deploy fails due to GitLab repositories not found](#terraform-deploy-fails-due-to-gitlab-repositories-not-found) -- [Error: Gitlab pipelines access denied](#gitlab-pipelines-access-denied) -- [Error: Unknown project id on 4-project step context](#error-unknown-project-id-on-4-project-step-context) -- [Error: Error getting operation for committing purpose for TagValue](#error-error-getting-operation-for-committing-purpose-for-tagvalue) -- [The user does not have permission to access Project or it may not exist](#the-user-does-not-have-permission-to-access-project-or-it-may-not-exist) +- [Troubleshooting](#troubleshooting) + - [Terminology](#terminology) + - [Problems](#problems) + - [Common issues](#common-issues) + - [Project quota exceeded](#project-quota-exceeded) + - [Default branch setting](#default-branch-setting) + - [Terraform State Snapshot lock](#terraform-state-snapshot-lock) + - [Downgrade your local Terraform version](#downgrade-your-local-terraform-version) + - [Upgrade your 0-bootstrap runner image Terraform version](#upgrade-your-0-bootstrap-runner-image-terraform-version) + - [Application authenticated using end user credentials](#application-authenticated-using-end-user-credentials) + - [Cannot assign requested address error in Cloud Shell](#cannot-assign-requested-address-error-in-cloud-shell) + - [Error: Unsupported attribute](#error-unsupported-attribute) + - [Error: Error adding network peering](#error-error-adding-network-peering) + - [Error: Unknown project id on 4-project step context](#error-unknown-project-id-on-4-project-step-context) + - [Error: Error getting operation for committing purpose for TagValue](#error-error-getting-operation-for-committing-purpose-for-tagvalue) + - [Caller does not have permission in the Organization](#caller-does-not-have-permission-in-the-organization) + - [Billing quota exceeded](#billing-quota-exceeded) + - [Terraform Error acquiring the state lock](#terraform-error-acquiring-the-state-lock) + - [VPC Service Controls](#vpc-service-controls) + - [Terraform deploy fails due to GitLab repositories not found](#terraform-deploy-fails-due-to-gitlab-repositories-not-found) + - [Gitlab pipelines access denied](#gitlab-pipelines-access-denied) + - [The user does not have permission to access Project or it may not exist](#the-user-does-not-have-permission-to-access-project-or-it-may-not-exist) - - - ### Project quota exceeded @@ -495,6 +506,102 @@ You can get this information from step `0-bootstrap` by running the following co - If you realize that the Terraform State lock was due to a build timeout increase the build timeout on [build configuration](https://github.com/terraform-google-modules/terraform-example-foundation/blob/master/build/cloudbuild-tf-apply.yaml#L15). +### VPC Service Controls + +**Error message:** + +```text +Failed to load state: Failed to open state file at gs://YOUR-TF-STATE-BUCKET/terraform/bootstrap/state/default.tfstate: googleapi: got HTTP response code 403 with body: SecurityPolicyViolatedRequest violates VPC Service Controls.
Request is prohibited by organization's policy. vpcServiceControlsUniqueIdentifier: VPC-UNIQUE-IDENTIFIER
+``` + +**Cause:** + +You are trying to access an output from a Terraform remote state stored in a project that is inside a VPC Service Controls perimeter. However, your current environment is configured with a billing/quota_project that is not part of that perimeter. Since the VPC Service Controls perimeter is in enforced mode, this configuration blocks access. + +**Solution:** + +Run the following gcloud commands to remove the configured billing/quota_project from your environment: + + ```bash + gcloud config unset billing/quota_project + gcloud auth application-default login + ``` + +This will ensure that requests are not made using the billing/quota_project configured in your environment, which is outside the VPC Service Controls perimeter and causes access to be blocked. + +If you must use a billing/quota_project outside the perimeter, you will need to add the following egress rule using the `egress_policies` variable for VPC Service Controls in enforced mode in the `1-org/envs/shared/terraform.tfvars` + +Steps: + +1. Navigate into the [gcp-org/envs/shared/terraform.tfvars](gcp-org/envs/shared/terraform.tfvars) and change to the `production` branch: + + ```bash + cd gcp-org/envs/shared/ + git checkout production + ``` + +- Update `egress_policies` variable, with the following rule: + + ``` + { + from = { + identities = [ + "user:YOUR-USER-EMAIL@example.com", + ] + sources = { + resources = [ + "projects/SEED_PROJECT_NUMBER" + ] + } + } + to = { + resources = [ + "projects/YOUR_BILLING_QUOTA_PROJECT" + ] + operations = { + "storage.googleapis.com" = { + methods = ["*"] + } + } + } + }, + ``` + +- Update the `egress_policies_keys` variable with the `"seed_to_billing_prj"` value. +- Update the identities field to match the values of the `perimeter_additional_members variable`. +- Run the following command to get the Seed project number and the Billing project configured in your environment project number. + + ```bash + export seed_project_number=$(terraform -chdir="../../../gcp-bootstrap/envs/shared/" output -raw seed_project_number) + echo "seed_project_number = ${seed_project_number}" + + export billing_quota_project_number=$(gcloud projects describe "$(gcloud config get-value billing/quota_project)" --format="value(projectNumber)") + echo "billing_quota_project_number = ${billing_quota_project_number}" + + sed -i'' -e "s/SEED_PROJECT_NUMBER/${seed_project_number}/" ./terraform.tfvars + sed -i'' -e "s/YOUR_BILLING_QUOTA_PROJECT/${billing_quota_project_number}/" ./terraform.tfvars + ``` + +- Use `terraform output` to get an environment variable `GOOGLE_IMPERSONATE_SERVICE_ACCOUNT` will be set using the Terraform Service Account to enable impersonation. +- Run `init`, `plan` and `apply` and review the output. +- Commit validated code. + + ```bash + cd ../../ + export GOOGLE_IMPERSONATE_SERVICE_ACCOUNT=$(terraform -chdir="../gcp-bootstrap/envs/shared/" output -raw organization_step_terraform_service_account_email) + echo ${GOOGLE_IMPERSONATE_SERVICE_ACCOUNT} + + ./tf-wrapper.sh init production + ./tf-wrapper.sh plan production + ./tf-wrapper.sh apply production + + git add . + git commit -m "Add egress rule for billig/quota project." + cd .. + + unset GOOGLE_IMPERSONATE_SERVICE_ACCOUNT + ``` + ### Terraform deploy fails due to GitLab repositories not found **Error message:** diff --git a/helpers/foundation-deployer/stages/apply.go b/helpers/foundation-deployer/stages/apply.go index 0667d2c9a..efc069dd4 100644 --- a/helpers/foundation-deployer/stages/apply.go +++ b/helpers/foundation-deployer/stages/apply.go @@ -201,8 +201,11 @@ func DeployBootstrapStage(t testing.TB, s steps.Steps, tfvars GlobalTFVars, c Co func DeployOrgStage(t testing.TB, s steps.Steps, tfvars GlobalTFVars, outputs BootstrapOutputs, c CommonConf) error { createACMAPolicy := testutils.GetOrgACMPolicyID(t, tfvars.OrgID) == "" + AccessContextManagerPolicyID := testutils.GetOrgACMPolicyID(t, tfvars.OrgID) orgTfvars := OrgTfvars{ + AccessContextManagerPolicyID: AccessContextManagerPolicyID, + PerimeterAdditionalMembers: tfvars.PerimeterAdditionalMembers, DomainsToAllow: tfvars.DomainsToAllow, EssentialContactsDomains: tfvars.EssentialContactsDomains, SccNotificationName: tfvars.SccNotificationName, @@ -312,9 +315,8 @@ func DeployNetworksStage(t testing.TB, s steps.Steps, tfvars GlobalTFVars, outpu } // common commonTfvars := NetCommonTfvars{ - Domain: tfvars.Domain, - PerimeterAdditionalMembers: tfvars.PerimeterAdditionalMembers, - RemoteStateBucket: outputs.RemoteStateBucket, + Domain: tfvars.Domain, + RemoteStateBucket: outputs.RemoteStateBucket, } if tfvars.EnableHubAndSpoke { commonTfvars.EnableHubAndSpokeTransitivity = &tfvars.EnableHubAndSpokeTransitivity @@ -323,14 +325,6 @@ func DeployNetworksStage(t testing.TB, s steps.Steps, tfvars GlobalTFVars, outpu if err != nil { return err } - //access_context - accessContextTfvars := NetAccessContextTfvars{ - AccessContextManagerPolicyID: testutils.GetOrgACMPolicyID(t, tfvars.OrgID), - } - err = utils.WriteTfvars(filepath.Join(c.FoundationPath, step, "access_context.auto.tfvars"), accessContextTfvars) - if err != nil { - return err - } conf := utils.CloneCSR(t, NetworksRepo, filepath.Join(c.CheckoutPath, NetworksRepo), outputs.CICDProject, c.Logger) stageConf := StageConf{ diff --git a/helpers/foundation-deployer/stages/data.go b/helpers/foundation-deployer/stages/data.go index 046d0cf0f..d018830fe 100644 --- a/helpers/foundation-deployer/stages/data.go +++ b/helpers/foundation-deployer/stages/data.go @@ -222,6 +222,8 @@ type BootstrapTfvars struct { } type OrgTfvars struct { + AccessContextManagerPolicyID string `hcl:"access_context_manager_policy_id"` + PerimeterAdditionalMembers []string `hcl:"perimeter_additional_members"` DomainsToAllow []string `hcl:"domains_to_allow"` EssentialContactsDomains []string `hcl:"essential_contacts_domains_to_allow"` SccNotificationName string `hcl:"scc_notification_name"` @@ -246,10 +248,9 @@ type EnvsTfvars struct { } type NetCommonTfvars struct { - Domain string `hcl:"domain"` - PerimeterAdditionalMembers []string `hcl:"perimeter_additional_members"` - RemoteStateBucket string `hcl:"remote_state_bucket"` - EnableHubAndSpokeTransitivity *bool `hcl:"enable_hub_and_spoke_transitivity"` + Domain string `hcl:"domain"` + RemoteStateBucket string `hcl:"remote_state_bucket"` + EnableHubAndSpokeTransitivity *bool `hcl:"enable_hub_and_spoke_transitivity"` } type NetSharedTfvars struct { @@ -260,10 +261,6 @@ type NetProductionTfvars struct { TargetNameServerAddresses []ServerAddress `hcl:"target_name_server_addresses"` } -type NetAccessContextTfvars struct { - AccessContextManagerPolicyID string `hcl:"access_context_manager_policy_id"` -} - type ProjCommonTfvars struct { RemoteStateBucket string `hcl:"remote_state_bucket"` } diff --git a/test/disable_tf_files.sh b/test/disable_tf_files.sh index 3f5438622..0be5f06c7 100755 --- a/test/disable_tf_files.sh +++ b/test/disable_tf_files.sh @@ -28,11 +28,6 @@ function networks(){ mv $network_dir/envs/production/production.auto.tfvars $network_dir/envs/production/production.auto.tfvars.disabled fi - # disable access_context.auto.tfvars in main module - mv $network_dir/envs/development/access_context.auto.tfvars $network_dir/envs/development/access_context.auto.tfvars.disabled - mv $network_dir/envs/nonproduction/access_context.auto.tfvars $network_dir/envs/nonproduction/access_context.auto.tfvars.disabled - mv $network_dir/envs/production/access_context.auto.tfvars $network_dir/envs/production/access_context.auto.tfvars.disabled - # disable common.auto.tfvars in main module mv $network_dir/envs/development/common.auto.tfvars $network_dir/envs/development/common.auto.tfvars.disabled mv $network_dir/envs/nonproduction/common.auto.tfvars $network_dir/envs/nonproduction/common.auto.tfvars.disabled @@ -50,9 +45,6 @@ function shared(){ network_dir="3-networks-svpc" fi - # disable access_context.auto.tfvars in main module - mv $network_dir/envs/shared/access_context.auto.tfvars $network_dir/envs/shared/access_context.auto.tfvars.disabled - # disable common.auto.tfvars in main module mv $network_dir/envs/shared/common.auto.tfvars $network_dir/envs/shared/common.auto.tfvars.disabled } diff --git a/test/integration/bootstrap/bootstrap_test.go b/test/integration/bootstrap/bootstrap_test.go index d9817529b..3927c4c68 100644 --- a/test/integration/bootstrap/bootstrap_test.go +++ b/test/integration/bootstrap/bootstrap_test.go @@ -164,7 +164,7 @@ func TestBootstrap(t *testing.T) { peeredNetworkName := testutils.GetLastSplitElement(bootstrap.GetStringOutput("cloud_build_peered_network_id"), "/") pool := gcloud.Runf(t, "builds worker-pools describe %s --region %s --project %s", workerPoolName, defaultRegion, cbProjectID) assert.Equal(workerPoolName, pool.Get("name").String(), "pool %s should exist", workerPoolName) - assert.Equal("PUBLIC_EGRESS", pool.Get("privatePoolV1Config.networkConfig.egressOption").String(), "pool %s should have internet access", workerPoolName) + assert.Equal("NO_PUBLIC_EGRESS", pool.Get("privatePoolV1Config.networkConfig.egressOption").String(), "pool %s should have internet access", workerPoolName) assert.Equal("e2-medium", pool.Get("privatePoolV1Config.workerConfig.machineType").String(), "pool %s should have the configured machineType", workerPoolName) assert.Equal("100", pool.Get("privatePoolV1Config.workerConfig.diskSizeGb").String(), "pool %s should have the configured disk size", workerPoolName) assert.Equal(peeredNetworkName, testutils.GetLastSplitElement(pool.Get("privatePoolV1Config.networkConfig.peeredNetwork").String(), "/"), "pool %s should have peered network configured", workerPoolName) diff --git a/test/integration/envs/envs_test.go b/test/integration/envs/envs_test.go old mode 100644 new mode 100755 index 15de28f31..4c5e28ab4 --- a/test/integration/envs/envs_test.go +++ b/test/integration/envs/envs_test.go @@ -16,12 +16,14 @@ package envs import ( "fmt" + "strings" "testing" "time" "github.com/GoogleCloudPlatform/cloud-foundation-toolkit/infra/blueprint-test/pkg/gcloud" "github.com/GoogleCloudPlatform/cloud-foundation-toolkit/infra/blueprint-test/pkg/tft" "github.com/GoogleCloudPlatform/cloud-foundation-toolkit/infra/blueprint-test/pkg/utils" + "github.com/gruntwork-io/terratest/modules/terraform" "github.com/stretchr/testify/assert" "github.com/terraform-google-modules/terraform-example-foundation/test/integration/testutils" @@ -65,6 +67,17 @@ func TestEnvs(t *testing.T) { tft.WithVars(vars), tft.WithBackendConfig(backendConfig), ) + + orgState := tft.NewTFBlueprintTest(t, + tft.WithTFDir("../../../1-org/envs/shared"), + tft.WithVars(vars), + tft.WithBackendConfig(backendConfig), + ) + + perimeterName := terraform.Output(t, orgState.GetTFOptions(), "service_perimeter_name") + orgID := bootstrap.GetTFSetupStringOutput("org_id") + policyID := testutils.GetOrgACMPolicyID(t, orgID) + envs.DefineVerify( func(assert *assert.Assertions) { // perform default verification ensuring Terraform reports no additional changes on an applied blueprint @@ -109,10 +122,15 @@ func TestEnvs(t *testing.T) { }, } { projectID := envs.GetStringOutput(projectEnvOutput.projectOutput) + projectNumber := envs.GetStringOutput(strings.ReplaceAll(projectEnvOutput.projectOutput, "_id", "_number")) prj := gcloud.Runf(t, "projects describe %s", projectID) assert.Equal(projectID, prj.Get("projectId").String(), fmt.Sprintf("project %s should exist", projectID)) assert.Equal("ACTIVE", prj.Get("lifecycleState").String(), fmt.Sprintf("project %s should be ACTIVE", projectID)) + perimeter, err := gcloud.RunCmdE(t, fmt.Sprintf("access-context-manager perimeters describe %s --policy %s", perimeterName, policyID)) + assert.NoError(err) + assert.True(strings.Contains(perimeter, projectNumber), fmt.Sprintf("dry-run service perimeter %s should contain project %s (number)", perimeterName, projectNumber)) + enabledAPIS := gcloud.Runf(t, "services list --project %s", projectID).Array() listApis := testutils.GetResultFieldStrSlice(enabledAPIS, "config.name") assert.Subset(listApis, projectEnvOutput.apis, "APIs should have been enabled") diff --git a/test/integration/networks/networks_test.go b/test/integration/networks/networks_test.go index e2f9c2039..2bf38aa67 100644 --- a/test/integration/networks/networks_test.go +++ b/test/integration/networks/networks_test.go @@ -24,9 +24,7 @@ import ( "github.com/GoogleCloudPlatform/cloud-foundation-toolkit/infra/blueprint-test/pkg/gcloud" "github.com/GoogleCloudPlatform/cloud-foundation-toolkit/infra/blueprint-test/pkg/tft" "github.com/GoogleCloudPlatform/cloud-foundation-toolkit/infra/blueprint-test/pkg/utils" - "github.com/gruntwork-io/terratest/modules/terraform" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" "github.com/terraform-google-modules/terraform-example-foundation/test/integration/testutils" ) @@ -75,11 +73,8 @@ func TestNetworks(t *testing.T) { tft.WithTFDir("../../../0-bootstrap"), ) - orgID := terraform.OutputMap(t, bootstrap.GetTFOptions(), "common_config")["org_id"] networkMode := getNetworkMode(t) firewallMode := getFirewallMode(t) - policyID := testutils.GetOrgACMPolicyID(t, orgID) - require.NotEmpty(t, policyID, "Access Context Manager Policy ID must be configured in the organization for the test to proceed.") // Configure impersonation for test execution terraformSA := bootstrap.GetStringOutput("networks_step_terraform_service_account_email") @@ -90,131 +85,6 @@ func TestNetworks(t *testing.T) { "bucket": backend_bucket, } - restrictedServices := []string{ - "accessapproval.googleapis.com", - "adsdatahub.googleapis.com", - "aiplatform.googleapis.com", - "alloydb.googleapis.com", - "documentai.googleapis.com", - "analyticshub.googleapis.com", - "apigee.googleapis.com", - "apigeeconnect.googleapis.com", - "artifactregistry.googleapis.com", - "assuredworkloads.googleapis.com", - "automl.googleapis.com", - "baremetalsolution.googleapis.com", - "batch.googleapis.com", - "bigquery.googleapis.com", - "bigquerydatapolicy.googleapis.com", - "bigquerydatatransfer.googleapis.com", - "bigquerymigration.googleapis.com", - "bigqueryreservation.googleapis.com", - "bigtable.googleapis.com", - "binaryauthorization.googleapis.com", - "cloud.googleapis.com", - "cloudasset.googleapis.com", - "cloudbuild.googleapis.com", - "clouddebugger.googleapis.com", - "clouddeploy.googleapis.com", - "clouderrorreporting.googleapis.com", - "cloudfunctions.googleapis.com", - "cloudkms.googleapis.com", - "cloudprofiler.googleapis.com", - "cloudresourcemanager.googleapis.com", - "cloudscheduler.googleapis.com", - "cloudsearch.googleapis.com", - "cloudtrace.googleapis.com", - "composer.googleapis.com", - "compute.googleapis.com", - "connectgateway.googleapis.com", - "contactcenterinsights.googleapis.com", - "container.googleapis.com", - "containeranalysis.googleapis.com", - "containerfilesystem.googleapis.com", - "containerregistry.googleapis.com", - "containerthreatdetection.googleapis.com", - "datacatalog.googleapis.com", - "dataflow.googleapis.com", - "datafusion.googleapis.com", - "datamigration.googleapis.com", - "dataplex.googleapis.com", - "dataproc.googleapis.com", - "datastream.googleapis.com", - "dialogflow.googleapis.com", - "dlp.googleapis.com", - "dns.googleapis.com", - "documentai.googleapis.com", - "domains.googleapis.com", - "eventarc.googleapis.com", - "file.googleapis.com", - "firebaseappcheck.googleapis.com", - "firebaserules.googleapis.com", - "firestore.googleapis.com", - "gameservices.googleapis.com", - "gkebackup.googleapis.com", - "gkeconnect.googleapis.com", - "gkehub.googleapis.com", - "healthcare.googleapis.com", - "iam.googleapis.com", - "iamcredentials.googleapis.com", - "iaptunnel.googleapis.com", - "ids.googleapis.com", - "integrations.googleapis.com", - "kmsinventory.googleapis.com", - "krmapihosting.googleapis.com", - "language.googleapis.com", - "lifesciences.googleapis.com", - "logging.googleapis.com", - "managedidentities.googleapis.com", - "memcache.googleapis.com", - "meshca.googleapis.com", - "meshconfig.googleapis.com", - "metastore.googleapis.com", - "ml.googleapis.com", - "monitoring.googleapis.com", - "networkconnectivity.googleapis.com", - "networkmanagement.googleapis.com", - "networksecurity.googleapis.com", - "networkservices.googleapis.com", - "notebooks.googleapis.com", - "opsconfigmonitoring.googleapis.com", - "orgpolicy.googleapis.com", - "osconfig.googleapis.com", - "oslogin.googleapis.com", - "privateca.googleapis.com", - "pubsub.googleapis.com", - "pubsublite.googleapis.com", - "recaptchaenterprise.googleapis.com", - "recommender.googleapis.com", - "redis.googleapis.com", - "retail.googleapis.com", - "run.googleapis.com", - "secretmanager.googleapis.com", - "servicecontrol.googleapis.com", - "servicedirectory.googleapis.com", - "spanner.googleapis.com", - "speakerid.googleapis.com", - "speech.googleapis.com", - "sqladmin.googleapis.com", - "storage.googleapis.com", - "storagetransfer.googleapis.com", - "sts.googleapis.com", - "texttospeech.googleapis.com", - "timeseriesinsights.googleapis.com", - "tpu.googleapis.com", - "trafficdirector.googleapis.com", - "transcoder.googleapis.com", - "translate.googleapis.com", - "videointelligence.googleapis.com", - "vision.googleapis.com", - "visionai.googleapis.com", - "vmmigration.googleapis.com", - "vpcaccess.googleapis.com", - "webrisk.googleapis.com", - "workflows.googleapis.com", - "workstations.googleapis.com", - } - cidrRanges := map[string][]string{ "development": []string{"10.8.64.0/18", "10.9.64.0/18"}, "nonproduction": []string{"10.8.128.0/18", "10.9.128.0/18"}, @@ -227,50 +97,6 @@ func TestNetworks(t *testing.T) { "production": "10.17.0.8", } - ingressPolicies := []map[string]interface{}{ - { - "from": map[string]interface{}{ - "sources": map[string][]string{ - "access_levels": {"*"}, - }, - "identity_type": "ANY_IDENTITY", - }, - "to": map[string]interface{}{ - "resources": []string{"*"}, - "operations": map[string]map[string][]string{ - "storage.googleapis.com": { - "methods": { - "google.storage.objects.get", - "google.storage.objects.list", - }, - }, - }, - }, - }, - } - - egressPolicies := []map[string]interface{}{ - { - "from": map[string]interface{}{ - "sources": map[string][]string{ - "access_levels": {"*"}, - }, - "identity_type": "ANY_IDENTITY", - }, - "to": map[string]interface{}{ - "resources": []string{"*"}, - "operations": map[string]map[string][]string{ - "storage.googleapis.com": { - "methods": { - "google.storage.objects.get", - "google.storage.objects.list", - }, - }, - }, - }, - }, - } - envStage := os.Getenv(utils.RUN_STAGE_ENV_VAR) var envNames []string @@ -293,11 +119,7 @@ func TestNetworks(t *testing.T) { t.Run(envName, func(t *testing.T) { vars := map[string]interface{}{ - "access_context_manager_policy_id": policyID, - "remote_state_bucket": backend_bucket, - "ingress_policies": ingressPolicies, - "egress_policies": egressPolicies, - "perimeter_additional_members": []string{}, + "remote_state_bucket": backend_bucket, } var tfdDir string @@ -327,19 +149,8 @@ func TestNetworks(t *testing.T) { // Resource issue: https://github.com/hashicorp/terraform-provider-google/issues/16804 // networks.DefaultVerify(assert) - servicePerimeterLink := fmt.Sprintf("accessPolicies/%s/servicePerimeters/%s", policyID, networks.GetStringOutput("service_perimeter_name")) - accessLevel := fmt.Sprintf("accessPolicies/%s/accessLevels/%s", policyID, networks.GetStringOutput("access_level_name_dry_run")) networkNames := getNetworkResourceNames(envCode, networkMode, firewallMode) - servicePerimeter, err := gcloud.RunCmdE(t, fmt.Sprintf("access-context-manager perimeters dry-run describe %s --policy %s", servicePerimeterLink, policyID)) - assert.NoError(err) - perimeterName := networks.GetStringOutput("service_perimeter_name") - assert.True(strings.Contains(servicePerimeter, perimeterName), fmt.Sprintf("service perimeter %s should exist", perimeterName)) - assert.True(strings.Contains(servicePerimeter, accessLevel), fmt.Sprintf("service perimeter %s should have access level %s", servicePerimeterLink, accessLevel)) - for _, service := range restrictedServices { - assert.True(strings.Contains(servicePerimeter, service), fmt.Sprintf("service perimeter %s should restrict all supported services", servicePerimeterLink)) - } - projectID := networks.GetStringOutput("shared_vpc_host_project_id") if strings.Contains(projectID, "-p-") && networkMode != "-spoke" { diff --git a/test/integration/org/org_test.go b/test/integration/org/org_test.go old mode 100644 new mode 100755 index 6246a4d42..f565c90ea --- a/test/integration/org/org_test.go +++ b/test/integration/org/org_test.go @@ -17,6 +17,7 @@ package org import ( "fmt" "strconv" + "strings" "testing" "time" @@ -38,21 +39,11 @@ func TestOrg(t *testing.T) { backend_bucket := bootstrap.GetStringOutput("gcs_bucket_tfstate") - vars := map[string]interface{}{ - "remote_state_bucket": backend_bucket, - "log_export_storage_force_destroy": "true", - "folder_deletion_protection": false, - "project_deletion_policy": "DELETE", - "enable_scc_resources_in_terraform": true, - } + ingressPolicies := []map[string]interface{}{} - backendConfig := map[string]interface{}{ - "bucket": backend_bucket, - } + egressPolicies := []map[string]interface{}{} - // Configure impersonation for test execution terraformSA := bootstrap.GetStringOutput("organization_step_terraform_service_account_email") - utils.SetEnv(t, "GOOGLE_IMPERSONATE_SERVICE_ACCOUNT", terraformSA) // Create Access Context Manager Policy ID if needed orgID := terraform.OutputMap(t, bootstrap.GetTFOptions(), "common_config")["org_id"] @@ -66,6 +57,152 @@ func TestOrg(t *testing.T) { } } + vars := map[string]interface{}{ + "remote_state_bucket": backend_bucket, + "log_export_storage_force_destroy": "true", + "folder_deletion_protection": false, + "project_deletion_policy": "DELETE", + "access_context_manager_policy_id": policyID, + "ingress_policies": ingressPolicies, + "egress_policies": egressPolicies, + "perimeter_additional_members": []string{}, + "enable_scc_resources_in_terraform": true, + } + + restrictedServices := []string{ + "serviceusage.googleapis.com", + "essentialcontacts.googleapis.com", + "accessapproval.googleapis.com", + "adsdatahub.googleapis.com", + "aiplatform.googleapis.com", + "alloydb.googleapis.com", + "documentai.googleapis.com", + "analyticshub.googleapis.com", + "apigee.googleapis.com", + "apigeeconnect.googleapis.com", + "artifactregistry.googleapis.com", + "assuredworkloads.googleapis.com", + "automl.googleapis.com", + "baremetalsolution.googleapis.com", + "batch.googleapis.com", + "bigquery.googleapis.com", + "bigquerydatapolicy.googleapis.com", + "bigquerydatatransfer.googleapis.com", + "bigquerymigration.googleapis.com", + "bigqueryreservation.googleapis.com", + "bigtable.googleapis.com", + "binaryauthorization.googleapis.com", + "cloud.googleapis.com", + "cloudasset.googleapis.com", + "cloudbuild.googleapis.com", + "clouddebugger.googleapis.com", + "clouddeploy.googleapis.com", + "clouderrorreporting.googleapis.com", + "cloudfunctions.googleapis.com", + "cloudkms.googleapis.com", + "cloudprofiler.googleapis.com", + "cloudresourcemanager.googleapis.com", + "cloudscheduler.googleapis.com", + "cloudsearch.googleapis.com", + "cloudtrace.googleapis.com", + "composer.googleapis.com", + "compute.googleapis.com", + "connectgateway.googleapis.com", + "contactcenterinsights.googleapis.com", + "container.googleapis.com", + "containeranalysis.googleapis.com", + "containerfilesystem.googleapis.com", + "containerregistry.googleapis.com", + "containerthreatdetection.googleapis.com", + "datacatalog.googleapis.com", + "dataflow.googleapis.com", + "datafusion.googleapis.com", + "datamigration.googleapis.com", + "dataplex.googleapis.com", + "dataproc.googleapis.com", + "datastream.googleapis.com", + "dialogflow.googleapis.com", + "dlp.googleapis.com", + "dns.googleapis.com", + "documentai.googleapis.com", + "domains.googleapis.com", + "eventarc.googleapis.com", + "file.googleapis.com", + "firebaseappcheck.googleapis.com", + "firebaserules.googleapis.com", + "firestore.googleapis.com", + "gameservices.googleapis.com", + "gkebackup.googleapis.com", + "gkeconnect.googleapis.com", + "gkehub.googleapis.com", + "healthcare.googleapis.com", + "iam.googleapis.com", + "iamcredentials.googleapis.com", + "iaptunnel.googleapis.com", + "ids.googleapis.com", + "integrations.googleapis.com", + "kmsinventory.googleapis.com", + "krmapihosting.googleapis.com", + "language.googleapis.com", + "lifesciences.googleapis.com", + "logging.googleapis.com", + "managedidentities.googleapis.com", + "memcache.googleapis.com", + "meshca.googleapis.com", + "meshconfig.googleapis.com", + "metastore.googleapis.com", + "ml.googleapis.com", + "monitoring.googleapis.com", + "networkconnectivity.googleapis.com", + "networkmanagement.googleapis.com", + "networksecurity.googleapis.com", + "networkservices.googleapis.com", + "notebooks.googleapis.com", + "opsconfigmonitoring.googleapis.com", + "orgpolicy.googleapis.com", + "osconfig.googleapis.com", + "oslogin.googleapis.com", + "privateca.googleapis.com", + "pubsub.googleapis.com", + "pubsublite.googleapis.com", + "recaptchaenterprise.googleapis.com", + "recommender.googleapis.com", + "redis.googleapis.com", + "retail.googleapis.com", + "run.googleapis.com", + "secretmanager.googleapis.com", + "servicecontrol.googleapis.com", + "servicedirectory.googleapis.com", + "spanner.googleapis.com", + "speakerid.googleapis.com", + "speech.googleapis.com", + "sqladmin.googleapis.com", + "storage.googleapis.com", + "storagetransfer.googleapis.com", + "sts.googleapis.com", + "texttospeech.googleapis.com", + "timeseriesinsights.googleapis.com", + "tpu.googleapis.com", + "trafficdirector.googleapis.com", + "transcoder.googleapis.com", + "translate.googleapis.com", + "videointelligence.googleapis.com", + "vision.googleapis.com", + "visionai.googleapis.com", + "vmmigration.googleapis.com", + "vpcaccess.googleapis.com", + "webrisk.googleapis.com", + "workflows.googleapis.com", + "workstations.googleapis.com", + } + + backendConfig := map[string]interface{}{ + "bucket": backend_bucket, + } + + // Configure impersonation for test execution + utils.SetEnv(t, "GOOGLE_IMPERSONATE_SERVICE_ACCOUNT", terraformSA) + org := tft.NewTFBlueprintTest(t, tft.WithTFDir("../../../1-org/envs/shared"), tft.WithVars(vars), @@ -123,7 +260,16 @@ func TestOrg(t *testing.T) { // check tags applied to common and bootstrap folder commonConfig := terraform.OutputMap(t, bootstrap.GetTFOptions(), "common_config") bootstrapFolder := testutils.GetLastSplitElement(commonConfig["bootstrap_folder_name"], "/") - + servicePerimeterLink := fmt.Sprintf("accessPolicies/%s/servicePerimeters/%s", policyID, org.GetStringOutput("service_perimeter_name")) + accessLevel := fmt.Sprintf("accessPolicies/%s/accessLevels/%s", policyID, org.GetStringOutput("access_level_name_dry_run")) + servicePerimeter, err := gcloud.RunCmdE(t, fmt.Sprintf("access-context-manager perimeters describe %s --policy %s", servicePerimeterLink, policyID)) + assert.NoError(err) + perimeterName := org.GetStringOutput("service_perimeter_name") + assert.True(strings.Contains(servicePerimeter, perimeterName), fmt.Sprintf("service perimeter %s should exist", perimeterName)) + assert.True(strings.Contains(servicePerimeter, accessLevel), fmt.Sprintf("service perimeter %s should have access level %s", servicePerimeterLink, accessLevel)) + for _, service := range restrictedServices { + assert.True(strings.Contains(servicePerimeter, service), fmt.Sprintf("service perimeter %s should restrict all supported services", servicePerimeterLink)) + } for _, tags := range []struct { folderId string folderName string @@ -378,6 +524,13 @@ func TestOrg(t *testing.T) { assert.Equal(sinkBilling.destination, logSinkBilling.Get("destination").String(), fmt.Sprintf("sink %s should have destination %s", sinkBilling.name, sinkBilling.destination)) } + // verify seed project in perimeter + sharedProjectNumber := bootstrap.GetStringOutput("seed_project_number") + perimeter := gcloud.Runf(t, "access-context-manager perimeters describe %s --policy %s", servicePerimeterLink, policyID) + projectFormat := fmt.Sprintf("projects/%s", sharedProjectNumber) + perimeterResources := utils.GetResultStrSlice(perimeter.Get("spec.resources").Array()) + assert.Contains(perimeterResources, projectFormat, fmt.Sprintf("dry-run service perimeter %s should contain %s", perimeterName, projectFormat)) + // hub and spoke infrastructure enable_hub_and_spoke, err := strconv.ParseBool(bootstrap.GetTFSetupStringOutput("enable_hub_and_spoke")) require.NoError(t, err) @@ -390,6 +543,12 @@ func TestOrg(t *testing.T) { projects := gcloud.Run(t, "projects list", gcOps).Array() assert.Equal(1, len(projects), fmt.Sprintf("project %s should exist", projectID)) assert.Equal("ACTIVE", projects[0].Get("lifecycleState").String(), fmt.Sprintf("project %s should be ACTIVE", projectID)) + numberOutput := strings.ReplaceAll(hubAndSpokeProjectOutput, "_id", "_number") // "net_hub_project_number" + projectNumber := org.GetStringOutput(numberOutput) + perimeter := gcloud.Runf(t, "access-context-manager perimeters describe %s --policy %s", servicePerimeterLink, policyID) + projectFormat := fmt.Sprintf("projects/%s", projectNumber) + perimeterResources := utils.GetResultStrSlice(perimeter.Get("spec.resources").Array()) + assert.Contains(perimeterResources, projectFormat, fmt.Sprintf("dry-run service perimeter %s should contain %s", perimeterName, projectFormat)) } } @@ -446,7 +605,10 @@ func TestOrg(t *testing.T) { projectID := org.GetStringOutput(projectOutput.output) prj := gcloud.Runf(t, "projects describe %s", projectID) assert.Equal("ACTIVE", prj.Get("lifecycleState").String(), fmt.Sprintf("project %s should be ACTIVE", projectID)) - + projectNumber := prj.Get("projectNumber").String() + perimeter, err := gcloud.RunCmdE(t, fmt.Sprintf("access-context-manager perimeters describe %s --policy %s", servicePerimeterLink, policyID)) + assert.NoError(err) + assert.True(strings.Contains(perimeter, projectNumber), fmt.Sprintf("dry-run service perimeter %s should contain project %s (number: %s)", perimeterName, projectID, projectNumber)) enabledAPIS := gcloud.Runf(t, "services list --project %s", projectID).Array() listApis := testutils.GetResultFieldStrSlice(enabledAPIS, "config.name") assert.Subset(listApis, projectOutput.apis, "APIs should have been enabled") @@ -477,10 +639,14 @@ func TestOrg(t *testing.T) { } { envProj := terraform.OutputMapOfObjects(t, org.GetTFOptions(), "shared_vpc_projects")[envName].(map[string]interface{}) projectID := envProj[projectEnvOutput.projectOutput] + projectNumber := fmt.Sprint(envProj["shared_vpc_project_number"]) prj := gcloud.Runf(t, "projects describe %s", projectID) assert.Equal(projectID, prj.Get("projectId").String(), fmt.Sprintf("project %s should exist", projectID)) assert.Equal("ACTIVE", prj.Get("lifecycleState").String(), fmt.Sprintf("project %s should be ACTIVE", projectID)) - + perimeter := gcloud.Runf(t, "access-context-manager perimeters describe %s --policy %s", servicePerimeterLink, policyID) + projectFormat := fmt.Sprintf("projects/%s", projectNumber) + perimeterResources := utils.GetResultStrSlice(perimeter.Get("spec.resources").Array()) + assert.Contains(perimeterResources, projectFormat, fmt.Sprintf("dry-run service perimeter %s should contain %s", perimeterName, projectFormat)) enabledAPIS := gcloud.Runf(t, "services list --project %s", projectID).Array() listApis := testutils.GetResultFieldStrSlice(enabledAPIS, "config.name") assert.Subset(listApis, projectEnvOutput.apis, "APIs should have been enabled") diff --git a/test/integration/projects-shared/projects_shared_test.go b/test/integration/projects-shared/projects_shared_test.go old mode 100644 new mode 100755 index 71c14681d..069bca1f3 --- a/test/integration/projects-shared/projects_shared_test.go +++ b/test/integration/projects-shared/projects_shared_test.go @@ -50,6 +50,16 @@ func TestProjectsShared(t *testing.T) { "cloudkms.googleapis.com", } + orgState := tft.NewTFBlueprintTest(t, + tft.WithTFDir("../../../1-org/envs/shared"), + tft.WithVars(map[string]interface{}{"remote_state_bucket": backend_bucket}), + tft.WithBackendConfig(map[string]interface{}{"bucket": backend_bucket}), + ) + perimeterName := terraform.Output(t, orgState.GetTFOptions(), "service_perimeter_name") + orgID := bootstrap.GetTFSetupStringOutput("org_id") + policyID := testutils.GetOrgACMPolicyID(t, orgID) + servicePerimeterLink := fmt.Sprintf("accessPolicies/%s/servicePerimeters/%s", policyID, perimeterName) + for _, tts := range []struct { name string repo string @@ -83,9 +93,15 @@ func TestProjectsShared(t *testing.T) { shared.DefaultVerify(assert) projectID := shared.GetStringOutput("cloudbuild_project_id") + projectNumber := shared.GetStringOutput("cloudbuild_project_number") prj := gcloud.Runf(t, "projects describe %s", projectID) assert.Equal("ACTIVE", prj.Get("lifecycleState").String(), fmt.Sprintf("project %s should be ACTIVE", projectID)) + projectFormat := fmt.Sprintf("projects/%s", projectNumber) + perimeter := gcloud.Runf(t, "access-context-manager perimeters describe %s --policy %s --format=json", servicePerimeterLink, policyID) + spec := utils.GetResultStrSlice(perimeter.Get("spec.resources").Array()) + assert.Contains(spec, projectFormat, fmt.Sprintf("dry-run service perimeter %s should contain %s", perimeterName, projectFormat)) + enabledAPIS := gcloud.Runf(t, "services list --project %s", projectID).Array() listApis := testutils.GetResultFieldStrSlice(enabledAPIS, "config.name") assert.Subset(listApis, sharedApisEnabled, "APIs should have been enabled") diff --git a/test/integration/projects/projects_test.go b/test/integration/projects/projects_test.go old mode 100644 new mode 100755 index 4edf875d7..6e7af0246 --- a/test/integration/projects/projects_test.go +++ b/test/integration/projects/projects_test.go @@ -104,23 +104,14 @@ func TestProjects(t *testing.T) { t.Parallel() env := testutils.GetLastSplitElement(tt.name, "_") - netVars := map[string]interface{}{ - "access_context_manager_policy_id": policyID, - } // networks created to retrieve output from the network step for this environment - var networkTFDir string - if networkMode == "" { - networkTFDir = "../../../3-networks-svpc/envs/%s" - } else { - networkTFDir = "../../../3-networks-hub-and-spoke/envs/%s" - } - networks := tft.NewTFBlueprintTest(t, - tft.WithTFDir(fmt.Sprintf(networkTFDir, env)), - tft.WithVars(netVars), + org := tft.NewTFBlueprintTest(t, + tft.WithTFDir("../../../1-org/envs/shared"), ) - perimeterName := networks.GetStringOutput("service_perimeter_name") + + perimeterName := org.GetStringOutput("service_perimeter_name") shared := tft.NewTFBlueprintTest(t, tft.WithTFDir(fmt.Sprintf(tt.baseDir, "shared")), @@ -161,9 +152,13 @@ func TestProjects(t *testing.T) { assert.Subset(listApis, restrictedApisEnabled, "APIs should have been enabled") sharedProjectNumber := projects.GetStringOutput("shared_vpc_project_number") - perimeter, err := gcloud.RunCmdE(t, fmt.Sprintf("access-context-manager perimeters dry-run describe %s --policy %s", perimeterName, policyID)) + floatingProjectNumber := projects.GetStringOutput("floating_project_number") + peeringProjectNumber := projects.GetStringOutput("peering_project_number") + perimeter, err := gcloud.RunCmdE(t, fmt.Sprintf("access-context-manager perimeters describe %s --policy %s", perimeterName, policyID)) assert.NoError(err) assert.True(strings.Contains(perimeter, sharedProjectNumber), fmt.Sprintf("dry-run service perimeter %s should contain project %s", perimeterName, sharedProjectNumber)) + assert.True(strings.Contains(perimeter, floatingProjectNumber), fmt.Sprintf("dry-run service perimeter %s should contain project %s", perimeterName, floatingProjectNumber)) + assert.True(strings.Contains(perimeter, peeringProjectNumber), fmt.Sprintf("dry-run service perimeter %s should contain project %s", perimeterName, peeringProjectNumber)) sharedVPC := gcloud.Runf(t, "compute shared-vpc get-host-project %s --impersonate-service-account %s", projectID, terraformSA) assert.NotEmpty(sharedVPC.Map()) diff --git a/test/integration/shared/shared_test.go b/test/integration/shared/shared_test.go index 1b1a73016..5447983aa 100644 --- a/test/integration/shared/shared_test.go +++ b/test/integration/shared/shared_test.go @@ -58,8 +58,6 @@ func TestShared(t *testing.T) { } var tfdDir string if isHubAndSpokeMode(t) { - vars["access_context_manager_policy_id"] = policyID - vars["perimeter_additional_members"] = []string{} tfdDir = "../../../3-networks-hub-and-spoke/envs/shared" } else { tfdDir = "../../../3-networks-svpc/envs/shared" diff --git a/test/integration/testutils/retry.go b/test/integration/testutils/retry.go old mode 100644 new mode 100755 index 362d3cdb5..ccac99c80 --- a/test/integration/testutils/retry.go +++ b/test/integration/testutils/retry.go @@ -41,7 +41,7 @@ var ( ".*Error 403.*Compute Engine API has not been used in project.*": "Compute Engine API not enabled", // Error 400: Service account {} does not exist. - ".*Error 400.*Service account.*does not exist*": "Error setting IAM policy", + ".*Error 400.*Service account.*does not exist.*": "Error setting IAM policy", // Error waiting for creating service network connection. This happens randomly for development, production and non-production environments ".*Error waiting for Create Service Networking Connection.*Error code 16.*Expected OAuth 2 access token.*": "Request had invalid authentication credentials.", diff --git a/test/restore_tf_files.sh b/test/restore_tf_files.sh old mode 100644 new mode 100755 index c089f459c..6ec7cbf04 --- a/test/restore_tf_files.sh +++ b/test/restore_tf_files.sh @@ -45,11 +45,6 @@ function networks(){ mv $network_dir/envs/nonproduction/backend.tf.disabled $network_dir/envs/nonproduction/backend.tf mv $network_dir/envs/production/backend.tf.disabled $network_dir/envs/production/backend.tf - # restore access_context.auto.tfvars in main module - mv $network_dir/envs/development/access_context.auto.tfvars.disabled $network_dir/envs/development/access_context.auto.tfvars - mv $network_dir/envs/nonproduction/access_context.auto.tfvars.disabled $network_dir/envs/nonproduction/access_context.auto.tfvars - mv $network_dir/envs/production/access_context.auto.tfvars.disabled $network_dir/envs/production/access_context.auto.tfvars - # restore common.auto.tfvars in main module mv $network_dir/envs/development/common.auto.tfvars.disabled $network_dir/envs/development/common.auto.tfvars mv $network_dir/envs/nonproduction/common.auto.tfvars.disabled $network_dir/envs/nonproduction/common.auto.tfvars @@ -70,9 +65,6 @@ function shared(){ # restore backend configs in main module mv $network_dir/envs/shared/backend.tf.disabled $network_dir/envs/shared/backend.tf - # restore access_context.auto.tfvars in main module - mv $network_dir/envs/shared/access_context.auto.tfvars.disabled $network_dir/envs/shared/access_context.auto.tfvars - # restore common.auto.tfvars in main module mv $network_dir/envs/shared/common.auto.tfvars.disabled $network_dir/envs/shared/common.auto.tfvars } @@ -84,11 +76,6 @@ function projects(){ mv 4-projects/business_unit_1/production/backend.tf.disabled 4-projects/business_unit_1/production/backend.tf mv 4-projects/business_unit_1/shared/backend.tf.disabled 4-projects/business_unit_1/shared/backend.tf - # restore access_context.auto.tfvars in main module - mv 4-projects/business_unit_1/development/access_context.auto.tfvars.disabled 4-projects/business_unit_1/development/access_context.auto.tfvars - mv 4-projects/business_unit_1/nonproduction/access_context.auto.tfvars.disabled 4-projects/business_unit_1/nonproduction/access_context.auto.tfvars - mv 4-projects/business_unit_1/production/access_context.auto.tfvars.disabled 4-projects/business_unit_1/production/access_context.auto.tfvars - # restore business_unit_1.auto.tfvars in main module mv 4-projects/business_unit_1/development/business_unit_1.auto.tfvars.disabled 4-projects/business_unit_1/development/business_unit_1.auto.tfvars mv 4-projects/business_unit_1/nonproduction/business_unit_1.auto.tfvars.disabled 4-projects/business_unit_1/nonproduction/business_unit_1.auto.tfvars