service_control.tf (248 lines of code) (raw):

/** * 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. */ locals { perimeter_members_data_ingestion = distinct(concat([ "serviceAccount:${module.data_ingestion_sa.dataflow_controller_service_account_email}", "serviceAccount:${module.data_ingestion_sa.storage_writer_service_account_email}", "serviceAccount:${module.data_ingestion_sa.pubsub_writer_service_account_email}", "serviceAccount:${google_project_service_identity.eventarc_identity_sa.email}", "serviceAccount:${google_project_service_identity.cloudbuild_identity_sa.email}", "serviceAccount:${google_project_service_identity.dlp_identity_sa.email}", "serviceAccount:${var.terraform_service_account}" ], var.perimeter_additional_members)) perimeter_members_governance = distinct(concat([ "serviceAccount:${var.terraform_service_account}" ], var.perimeter_additional_members)) perimeter_members_data = distinct(concat([ "serviceAccount:${var.terraform_service_account}" ], var.perimeter_additional_members)) supported_restricted_service = [ "accessapproval.googleapis.com", "adsdatahub.googleapis.com", "aiplatform.googleapis.com", "alloydb.googleapis.com", "alpha-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", ] restricted_services = length(var.custom_restricted_services) != 0 ? var.custom_restricted_services : local.supported_restricted_service actual_policy = var.access_context_manager_policy_id != "" ? var.access_context_manager_policy_id : google_access_context_manager_access_policy.access_policy[0].name data_ingestion_default_ingress_rule = var.sdx_project_number == "" ? [] : [ # You can add here default ingress policies if necessary ] data_ingestion_default_egress_rule = var.sdx_project_number == "" ? [] : [ { "from" = { "identities" = distinct(concat( var.data_ingestion_dataflow_deployer_identities, ["serviceAccount:${var.terraform_service_account}"], ["serviceAccount:${module.data_ingestion_sa.dataflow_controller_service_account_email}"] )) }, "to" = { "resources" = ["projects/${var.sdx_project_number}"] "operations" = { "storage.googleapis.com" = { "methods" = [ "google.storage.objects.get" ] } "artifactregistry.googleapis.com" = { "methods" = [ "*" ] } } } }, ] data_governance_default_ingress_rule = var.sdx_project_number == "" ? [] : [ # You can add here default ingress policies if necessary ] data_governance_default_egress_rule = var.sdx_project_number == "" ? [] : [ # You can add here default egress policies if necessary ] data_default_ingress_rule = var.sdx_project_number == "" ? [] : [ { "from" = { "identities" = distinct(concat( ["serviceAccount:service-${var.data_ingestion_project_number}@gcp-sa-pubsub.iam.gserviceaccount.com"] )), "sources" = { "resources" = ["projects/${var.data_ingestion_project_number}"] } }, "to" = { "resources" = ["*"], "operations" = { "bigquery.googleapis.com" = { "methods" = [ "*" ] } } } } ] data_default_egress_rule = var.sdx_project_number == "" ? [] : [ # You can add here default egress policies if necessary ] } resource "google_project_iam_member" "bigquery_metadata_viewer_binding" { project = var.data_project_id role = "roles/bigquery.metadataViewer" member = "serviceAccount:service-${var.data_ingestion_project_number}@gcp-sa-pubsub.iam.gserviceaccount.com" } resource "google_project_iam_member" "bigquery_data_editor_binding" { project = var.data_project_id role = "roles/bigquery.dataEditor" member = "serviceAccount:service-${var.data_ingestion_project_number}@gcp-sa-pubsub.iam.gserviceaccount.com" } resource "google_project_service_identity" "eventarc_identity_sa" { provider = google-beta project = var.data_ingestion_project_id service = "eventarc.googleapis.com" } resource "google_project_service_identity" "cloudbuild_identity_sa" { provider = google-beta project = var.data_ingestion_project_id service = "cloudbuild.googleapis.com" } resource "google_project_service_identity" "dlp_identity_sa" { provider = google-beta project = var.data_project_id service = "dlp.googleapis.com" } resource "google_access_context_manager_access_policy" "access_policy" { count = var.access_context_manager_policy_id != "" ? 0 : 1 parent = "organizations/${var.org_id}" title = "default policy" } resource "random_id" "suffix" { byte_length = 4 } // It's necessary to use the forces_wait_propagation to guarantee the resources that use this VPC do not have issues related to the propagation. // See: https://cloud.google.com/vpc-service-controls/docs/manage-service-perimeters#update. resource "time_sleep" "forces_wait_propagation" { destroy_duration = "330s" depends_on = [ module.data_ingestion_sa, module.data_governance_sa, google_project_iam_member.data-engineer-group-ingestion, google_project_iam_member.data-engineer-group, google_project_iam_member.data-analyst-group-ingestion, google_project_iam_member.data-analyst-group, google_organization_iam_member.security-analyst-group, google_organization_iam_member.network-administrator-group, google_organization_iam_member.security-administrator-group, google_project_iam_member.plaintext_reader_group, google_project_iam_member.encrypted_data_reader_group, ] } module "data_ingestion_vpc_sc" { source = ".//modules/vpc-sc-config" count = var.data_ingestion_perimeter == "" ? 1 : 0 access_context_manager_policy_id = local.actual_policy common_name = "data_ingestion" common_suffix = random_id.suffix.hex perimeter_members = local.perimeter_members_data_ingestion restricted_services = local.restricted_services access_level_ip_subnetworks = var.access_level_ip_subnetworks resources = { data_ingestion = var.data_ingestion_project_number } ingress_policies = distinct(concat( local.data_ingestion_default_ingress_rule, var.data_ingestion_ingress_policies )) egress_policies = distinct(concat( local.data_ingestion_default_egress_rule, var.data_ingestion_egress_policies )) # depends_on block is needed to prevent possible propagation errors # e.g. VPC-SC perimeter is created but its members not yet propagated. depends_on = [ time_sleep.forces_wait_propagation ] } # Adding project to an existing VPC Service Controls Perimeter # instead of the default VPC Service Controls perimeter. # The default VPC Service Controls perimeter and access list will not be created. resource "google_access_context_manager_service_perimeter_resource" "ingestion-perimeter-resource" { count = var.data_ingestion_perimeter != "" && var.add_project_to_data_ingestion_perimeter ? 1 : 0 perimeter_name = "accessPolicies/${local.actual_policy}/servicePerimeters/${var.data_ingestion_perimeter}" resource = "projects/${var.data_ingestion_project_number}" depends_on = [ time_sleep.forces_wait_propagation ] } module "data_governance_vpc_sc" { source = ".//modules/vpc-sc-config" count = var.data_governance_perimeter == "" ? 1 : 0 access_context_manager_policy_id = local.actual_policy common_name = "data_governance" common_suffix = random_id.suffix.hex perimeter_members = local.perimeter_members_governance restricted_services = local.restricted_services access_level_ip_subnetworks = var.access_level_ip_subnetworks resources = { data_governance = var.data_governance_project_number } ingress_policies = distinct(concat( local.data_governance_default_ingress_rule, var.data_governance_ingress_policies )) egress_policies = distinct(concat( local.data_governance_default_egress_rule, var.data_governance_egress_policies )) # depends_on block is needed to prevent possible propagation errors # e.g. VPC-SC perimeter is created but its members not yet propagated. depends_on = [ module.data_governance_sa, time_sleep.forces_wait_propagation ] } # Adding project to an existing VPC Service Controls Perimeter # instead of the default VPC Service Controls perimeter. # The default VPC Service Controls perimeter and access list will not be created. resource "google_access_context_manager_service_perimeter_resource" "governance-perimeter-resource" { count = var.data_governance_perimeter != "" && var.add_project_to_data_governance_perimeter ? 1 : 0 perimeter_name = "accessPolicies/${local.actual_policy}/servicePerimeters/${var.data_governance_perimeter}" resource = "projects/${var.data_governance_project_number}" depends_on = [ time_sleep.forces_wait_propagation ] } module "data_vpc_sc" { source = ".//modules/vpc-sc-config" count = var.data_perimeter == "" ? 1 : 0 access_context_manager_policy_id = local.actual_policy common_name = "data" common_suffix = random_id.suffix.hex perimeter_members = local.perimeter_members_data restricted_services = local.restricted_services access_level_ip_subnetworks = var.access_level_ip_subnetworks resources = { data = var.data_project_number } ingress_policies = distinct(concat( local.data_default_ingress_rule, var.data_ingress_policies )) egress_policies = distinct(concat( local.data_default_egress_rule, var.data_egress_policies )) # depends_on block is needed to prevent possible propagation errors # e.g. VPC-SC perimeter is created but its members not yet propagated. depends_on = [ time_sleep.forces_wait_propagation ] } # Adding project to an existing VPC Service Controls Perimeter # instead of the default VPC Service Controls perimeter. # The default VPC Service Controls perimeter and access list will not be created. resource "google_access_context_manager_service_perimeter_resource" "perimeter-resource" { count = var.data_perimeter != "" && var.add_project_to_data_perimeter ? 1 : 0 perimeter_name = "accessPolicies/${local.actual_policy}/servicePerimeters/${var.data_perimeter}" resource = "projects/${var.data_project_number}" depends_on = [ time_sleep.forces_wait_propagation ] } module "vpc_sc_bridge_data_ingestion_governance" { source = "terraform-google-modules/vpc-service-controls/google//modules/bridge_service_perimeter" version = "4.0.1" policy = local.actual_policy perimeter_name = "vpc_sc_bridge_ingestion_to_governance_${random_id.suffix.hex}" description = "VPC-SC bridge between ingestion and governance perimeters" resources = [ var.data_ingestion_project_number, var.data_governance_project_number, ] resource_keys = [ 0, 1 ] depends_on = [ time_sleep.forces_wait_propagation, module.data_governance_vpc_sc, module.data_ingestion_vpc_sc, google_access_context_manager_service_perimeter_resource.ingestion-perimeter-resource, google_access_context_manager_service_perimeter_resource.governance-perimeter-resource, ] } module "vpc_sc_bridge_data_governance" { source = "terraform-google-modules/vpc-service-controls/google//modules/bridge_service_perimeter" version = "4.0.1" policy = local.actual_policy perimeter_name = "vpc_sc_bridge_data_to_governance_${random_id.suffix.hex}" description = "VPC-SC bridge between data and governance perimeters" resources = [ var.data_project_number, var.data_governance_project_number ] resource_keys = [ 0, 1 ] depends_on = [ time_sleep.forces_wait_propagation, module.data_vpc_sc, module.data_governance_vpc_sc, google_access_context_manager_service_perimeter_resource.perimeter-resource, google_access_context_manager_service_perimeter_resource.governance-perimeter-resource, ] } module "vpc_sc_bridge_data_ingestion" { source = "terraform-google-modules/vpc-service-controls/google//modules/bridge_service_perimeter" version = "4.0.1" policy = local.actual_policy perimeter_name = "vpc_sc_bridge_data_to_ingestion_${random_id.suffix.hex}" description = "VPC-SC bridge between data and ingestion perimeters" resources = [ var.data_project_number, var.data_ingestion_project_number ] resource_keys = [ 0, 1 ] depends_on = [ time_sleep.forces_wait_propagation, module.data_vpc_sc, module.data_ingestion_vpc_sc, google_access_context_manager_service_perimeter_resource.perimeter-resource, google_access_context_manager_service_perimeter_resource.ingestion-perimeter-resource, ] } resource "time_sleep" "wait_for_bridge_propagation" { create_duration = "240s" depends_on = [ module.vpc_sc_bridge_data_ingestion, module.vpc_sc_bridge_data_governance, module.vpc_sc_bridge_data_ingestion_governance ] }