From afe6228042a278b81e9a80a02bf0f8d2a763e1fc Mon Sep 17 00:00:00 2001 From: Amanda Karina Lopes de Oliveira Date: Thu, 20 Apr 2023 08:39:05 -0300 Subject: [PATCH] adds support to multiple services project and the creation of a Shared VPC in a network project --- examples/secure_cloud_run_standalone/main.tf | 14 +- .../secure_cloud_run_standalone/outputs.tf | 12 +- modules/secure-cloud-run/main.tf | 3 +- .../README.md | 18 ++- .../main.tf | 133 +++++++++--------- .../network.tf | 26 +++- .../outputs.tf | 44 ++++-- .../private_service_connect.tf | 6 +- .../service_perimeter.tf | 34 ++++- .../variables.tf | 22 ++- .../versions.tf | 4 +- .../README.md | 1 + .../firewall.tf | 0 .../iam.tf | 4 + .../network.tf | 0 .../outputs.tf | 0 .../variables.tf | 5 + .../versions.tf | 0 modules/service-project-factory/main.tf | 91 ++++++++++++ modules/service-project-factory/outputs.tf | 45 ++++++ modules/service-project-factory/variables.tf | 57 ++++++++ 21 files changed, 401 insertions(+), 118 deletions(-) rename modules/{secure-cloud-serverless-harness => secure-serverless-harness}/README.md (90%) rename modules/{secure-cloud-serverless-harness => secure-serverless-harness}/main.tf (58%) rename modules/{secure-cloud-serverless-harness => secure-serverless-harness}/network.tf (68%) rename modules/{secure-cloud-serverless-harness => secure-serverless-harness}/outputs.tf (65%) rename modules/{secure-cloud-serverless-harness => secure-serverless-harness}/private_service_connect.tf (86%) rename modules/{secure-cloud-serverless-harness => secure-serverless-harness}/service_perimeter.tf (85%) rename modules/{secure-cloud-serverless-harness => secure-serverless-harness}/variables.tf (92%) rename modules/{secure-cloud-serverless-harness => secure-serverless-harness}/versions.tf (94%) rename modules/{secure-cloud-serverless-net => secure-serverless-net}/README.md (97%) rename modules/{secure-cloud-serverless-net => secure-serverless-net}/firewall.tf (100%) rename modules/{secure-cloud-serverless-net => secure-serverless-net}/iam.tf (95%) rename modules/{secure-cloud-serverless-net => secure-serverless-net}/network.tf (100%) rename modules/{secure-cloud-serverless-net => secure-serverless-net}/outputs.tf (100%) rename modules/{secure-cloud-serverless-net => secure-serverless-net}/variables.tf (94%) rename modules/{secure-cloud-serverless-net => secure-serverless-net}/versions.tf (100%) create mode 100644 modules/service-project-factory/main.tf create mode 100644 modules/service-project-factory/outputs.tf create mode 100644 modules/service-project-factory/variables.tf diff --git a/examples/secure_cloud_run_standalone/main.tf b/examples/secure_cloud_run_standalone/main.tf index 242e066a..8113a747 100644 --- a/examples/secure_cloud_run_standalone/main.tf +++ b/examples/secure_cloud_run_standalone/main.tf @@ -27,10 +27,10 @@ resource "random_id" "random_folder_suffix" { } module "secure_harness" { - source = "../../modules/secure-cloud-serverless-harness" + source = "../../modules/secure-serverless-harness" billing_account = var.billing_account security_project_name = "prj-kms-secure-cloud-run" - serverless_project_name = "prj-secure-cloud-run" + serverless_project_names = ["prj-secure-cloud-run"] org_id = var.org_id parent_folder_id = var.parent_folder_id serverless_folder_suffix = random_id.random_folder_suffix.hex @@ -65,18 +65,18 @@ module "secure_cloud_run" { source = "../../modules/secure-cloud-run" location = local.location region = local.region - serverless_project_id = module.secure_harness.serverless_project_id - vpc_project_id = module.secure_harness.serverless_project_id + serverless_project_id = module.secure_harness.serverless_project_ids[0] + vpc_project_id = module.secure_harness.network_project_id[0] kms_project_id = module.secure_harness.security_project_id key_name = "key-secure-cloud-run" keyring_name = "krg-secure-cloud-run" service_name = "srv-secure-cloud-run" image = "${local.location}-docker.pkg.dev/${module.secure_harness.security_project_id}/${module.secure_harness.artifact_registry_repository_name}/hello:latest" - cloud_run_sa = module.secure_harness.service_account_email + cloud_run_sa = module.secure_harness.service_account_email[module.secure_harness.serverless_project_ids[0]] connector_name = "con-secure-cloud-run" - subnet_name = module.secure_harness.service_subnet + subnet_name = module.secure_harness.service_subnet[0] create_subnet = false - shared_vpc_name = module.secure_harness.service_vpc.network_name + shared_vpc_name = module.secure_harness.service_vpc[0].network_name ip_cidr_range = "10.0.0.0/28" prevent_destroy = false artifact_registry_repository_location = local.location diff --git a/examples/secure_cloud_run_standalone/outputs.tf b/examples/secure_cloud_run_standalone/outputs.tf index ded60f39..9e7a14b1 100644 --- a/examples/secure_cloud_run_standalone/outputs.tf +++ b/examples/secure_cloud_run_standalone/outputs.tf @@ -15,12 +15,12 @@ */ output "serverless_project_id" { - value = module.secure_harness.serverless_project_id + value = module.secure_harness.serverless_project_ids[0] description = "The serverless project id." } output "serverless_project_number" { - value = module.secure_harness.serverless_project_number + value = module.secure_harness.serverless_project_numbers[module.secure_harness.serverless_project_ids[0]] description = "The serverless project number." } @@ -35,22 +35,22 @@ output "security_project_number" { } output "service_account_email" { - value = module.secure_harness.service_account_email + value = module.secure_harness.service_account_email[module.secure_harness.serverless_project_ids[0]] description = "The service account email created to be used by Cloud Run." } output "service_vpc_self_link" { - value = module.secure_harness.service_vpc.network.self_link + value = module.secure_harness.service_vpc[0].network.self_link description = "The Network self-link created in harness." } output "service_vpc_name" { - value = module.secure_harness.service_vpc.network_name + value = module.secure_harness.service_vpc[0].network_name description = "The Network self-link created in harness." } output "service_vpc_subnet_name" { - value = module.secure_harness.service_subnet + value = module.secure_harness.service_subnet[0] description = "The sub-network name created in harness." } diff --git a/modules/secure-cloud-run/main.tf b/modules/secure-cloud-run/main.tf index ccb9c113..47756ca8 100644 --- a/modules/secure-cloud-run/main.tf +++ b/modules/secure-cloud-run/main.tf @@ -44,7 +44,7 @@ module "vpc_project_apis" { } module "cloud_run_network" { - source = "../secure-cloud-serverless-net" + source = "../secure-serverless-net" connector_name = var.connector_name subnet_name = var.subnet_name @@ -56,6 +56,7 @@ module "cloud_run_network" { ip_cidr_range = var.ip_cidr_range create_subnet = var.create_subnet resource_names_suffix = var.resource_names_suffix + serverless_type = "CLOUD_RUN" serverless_service_identity_email = google_project_service_identity.serverless_sa.email diff --git a/modules/secure-cloud-serverless-harness/README.md b/modules/secure-serverless-harness/README.md similarity index 90% rename from modules/secure-cloud-serverless-harness/README.md rename to modules/secure-serverless-harness/README.md index c58dad57..f8aa268e 100644 --- a/modules/secure-cloud-serverless-harness/README.md +++ b/modules/secure-serverless-harness/README.md @@ -70,6 +70,7 @@ module "secure_cloud_run_harness" { | key\_rotation\_period | Period of key rotation in seconds. Default value is equivalent to 30 days. | `string` | `"2592000s"` | no | | keyring\_name | Keyring name. | `string` | n/a | yes | | location | The location where resources are going to be deployed. | `string` | n/a | yes | +| network\_project\_name | The name to give the shared vpc project. | `string` | `""` | no | | org\_id | The organization ID. | `string` | n/a | yes | | owners | List of comma-separated owners for each key declared in set\_owners\_for. | `list(string)` | `[]` | no | | parent\_folder\_id | The ID of a folder to host the infrastructure created in this module. | `string` | `""` | no | @@ -78,10 +79,11 @@ module "secure_cloud_run_harness" { | region | The region in which the subnetwork will be created. | `string` | n/a | yes | | security\_project\_name | The name to give the security project. | `string` | n/a | yes | | serverless\_folder\_suffix | The suffix to be concat in the Serverless folder name fldr-serverless-. | `string` | `""` | no | -| serverless\_project\_name | The name to give the Cloud Run project. | `string` | n/a | yes | +| serverless\_project\_names | The name to give the Cloud Serverless project. | `list(string)` | n/a | yes | | serverless\_type | The type of resource to be used. It supports only CLOUD\_RUN or CLOUD\_FUNCTION | `string` | n/a | yes | -| service\_account\_project\_roles | Common roles to apply to the Cloud Run service account in the serverless project. | `list(string)` | `[]` | no | +| service\_account\_project\_roles | Common roles to apply to the Cloud Serverless service account in the serverless project. | `map(list(string))` | `{}` | no | | subnet\_ip | The CDIR IP range of the subnetwork. | `string` | n/a | yes | +| use\_shared\_vpc | Defines if the network created will be a single or shared vpc. | `bool` | `false` | no | | vpc\_name | The name of the network. | `string` | n/a | yes | ## Outputs @@ -90,17 +92,19 @@ module "secure_cloud_run_harness" { |------|-------------| | artifact\_registry\_repository\_id | The Artifact Registry Repository full identifier where the images should be stored. | | artifact\_registry\_repository\_name | The Artifact Registry Repository last part of the repository name where the images should be stored. | -| cloud\_run\_service\_identity\_email | The Cloud Run Service Identity email. | +| cloud\_serverless\_service\_identity\_email | The Cloud Run Service Identity email. | +| cloudfunction\_source\_bucket | Cloud Function Source Bucket. | +| network\_project\_id | Project ID of the project created to host the Cloud Run Network. | | restricted\_access\_level\_name | Access level name. | | restricted\_service\_perimeter\_name | Service Perimeter name. | | security\_project\_id | Project ID of the project created for KMS and Artifact Register. | | security\_project\_number | Project number of the project created for KMS and Artifact Register. | | serverless\_folder\_id | The folder created to alocate Serverless infra. | -| serverless\_project\_id | Project ID of the project created to deploy Cloud Run. | -| serverless\_project\_number | Project number of the project created to deploy Cloud Run. | -| service\_account\_email | The email of the Service Account created to be used by Cloud Run. | +| serverless\_project\_ids | Project ID of the projects created to deploy Cloud Run. | +| serverless\_project\_numbers | Project number of the projects created to deploy Cloud Run. | +| service\_account\_email | The email of the Service Account created to be used by Cloud Serverless. | | service\_subnet | The sub-network name created in harness. | -| service\_vpc | The network created for Cloud Run. | +| service\_vpc | The network created for Cloud Serverless. | diff --git a/modules/secure-cloud-serverless-harness/main.tf b/modules/secure-serverless-harness/main.tf similarity index 58% rename from modules/secure-cloud-serverless-harness/main.tf rename to modules/secure-serverless-harness/main.tf index bef51058..050e6e5e 100644 --- a/modules/secure-cloud-serverless-harness/main.tf +++ b/modules/secure-serverless-harness/main.tf @@ -15,23 +15,33 @@ */ locals { - api = var.serverless_type == "CLOUD_RUN" ? "run" : "cloudfunctions" - serverless_apis = [ + api = var.serverless_type == "CLOUD_FUNCTION" ? ["cloudfunctions.googleapis.com", "cloudbuild.googleapis.com", "eventarc.googleapis.com"] : [] + serverless_apis = concat([ "vpcaccess.googleapis.com", "compute.googleapis.com", "container.googleapis.com", "artifactregistry.googleapis.com", - "${local.api}.googleapis.com", + "run.googleapis.com", "cloudkms.googleapis.com", "dns.googleapis.com" - ] + ], local.api) kms_apis = [ "cloudkms.googleapis.com", "artifactregistry.googleapis.com" ] - decrypters = join(",", concat(["serviceAccount:${google_project_service_identity.artifact_sa.email}"], var.decrypters)) - encrypters = join(",", concat(["serviceAccount:${google_project_service_identity.artifact_sa.email}"], var.encrypters)) + network_apis = [ + "vpcaccess.googleapis.com", + "compute.googleapis.com", + "dns.googleapis.com" + ] + + network_project_id = var.use_shared_vpc ? module.network_project[0].project_id : "" + + eventarc_identities = [for project in module.serverless_project : "serviceAccount:${project.services_identities["eventarc"]}"] + gcs_identities = [for project in module.serverless_project : "serviceAccount:${project.services_identities["gcs"]}"] + decrypters = join(",", concat(["serviceAccount:${google_project_service_identity.artifact_sa.email}"], local.eventarc_identities, local.gcs_identities, var.decrypters)) + encrypters = join(",", concat(["serviceAccount:${google_project_service_identity.artifact_sa.email}"], local.eventarc_identities, local.gcs_identities, var.encrypters)) } @@ -40,82 +50,45 @@ resource "google_folder" "fld_serverless" { parent = var.parent_folder_id == "" ? "organizations/${var.org_id}" : "folders/${var.parent_folder_id}" } -module "security_project" { +module "network_project" { + count = var.use_shared_vpc ? 1 : 0 source = "terraform-google-modules/project-factory/google" - version = "~> 13.0" + version = "~> 14.2" random_project_id = "true" - activate_apis = local.kms_apis - name = var.security_project_name + activate_apis = local.network_apis + name = var.network_project_name org_id = var.org_id billing_account = var.billing_account folder_id = google_folder.fld_serverless.name + + enable_shared_vpc_host_project = true } -module "serverless_project" { +module "security_project" { source = "terraform-google-modules/project-factory/google" - version = "~> 13.0" + version = "~> 14.2" random_project_id = "true" - activate_apis = local.serverless_apis - name = var.serverless_project_name + activate_apis = local.kms_apis + name = var.security_project_name org_id = var.org_id billing_account = var.billing_account folder_id = google_folder.fld_serverless.name } -module "service_accounts" { - source = "terraform-google-modules/service-accounts/google" - version = "~> 3.0" - project_id = module.serverless_project.project_id - prefix = "sa" - names = ["serverless-${local.api}"] - - depends_on = [ - time_sleep.wait_90_seconds - ] -} - -resource "google_project_iam_member" "cloud_run_sa_roles" { - for_each = toset(var.service_account_project_roles) - project = module.serverless_project.project_id - role = each.value - member = module.service_accounts.iam_email - - depends_on = [ - time_sleep.wait_90_seconds - ] -} - -resource "google_project_service_identity" "serverless_sa" { - provider = google-beta - - project = module.serverless_project.project_id - service = "${local.api}.googleapis.com" - - depends_on = [ - time_sleep.wait_90_seconds - ] -} +module "serverless_project" { + source = "../service-project-factory" -resource "google_service_account_iam_member" "identity_service_account_user" { - service_account_id = module.service_accounts.service_account.id - role = "roles/iam.serviceAccountUser" - member = "serviceAccount:${google_project_service_identity.serverless_sa.email}" + for_each = toset(var.serverless_project_names) - depends_on = [ - time_sleep.wait_90_seconds - ] + billing_account = var.billing_account + serverless_type = var.serverless_type + org_id = var.org_id + activate_apis = local.serverless_apis + folder_name = google_folder.fld_serverless.name + project_name = each.value + service_account_project_roles = length(var.service_account_project_roles) > 0 ? var.service_account_project_roles[each.value] : [] } -resource "google_project_service_identity" "artifact_sa" { - provider = google-beta - - project = module.security_project.project_id - service = "artifactregistry.googleapis.com" - - depends_on = [ - time_sleep.wait_90_seconds - ] -} resource "google_artifact_registry_repository" "repo" { project = module.security_project.project_id @@ -131,11 +104,12 @@ resource "google_artifact_registry_repository" "repo" { } resource "google_artifact_registry_repository_iam_member" "member" { + for_each = module.serverless_project project = module.security_project.project_id location = var.location repository = google_artifact_registry_repository.repo.repository_id role = "roles/artifactregistry.reader" - member = "serviceAccount:${google_project_service_identity.serverless_sa.email}" + member = "serviceAccount:${each.value.cloud_serverless_service_identity_email}" depends_on = [ time_sleep.wait_90_seconds @@ -164,3 +138,32 @@ module "artifact_registry_kms" { time_sleep.wait_90_seconds ] } + +resource "google_project_service_identity" "artifact_sa" { + provider = google-beta + + project = module.security_project.project_id + service = "artifactregistry.googleapis.com" + + depends_on = [ + time_sleep.wait_90_seconds + ] +} + +module "cloudfunction_source_bucket" { + for_each = var.serverless_type == "CLOUD_RUN" ? {} : module.serverless_project + source = "terraform-google-modules/cloud-storage/google//modules/simple_bucket" + version = "~>3.4" + + project_id = each.value.project_id + name = "bkt-${var.location}-${each.value.project_number}-cfv2-zip-files" + location = var.location + storage_class = "REGIONAL" + force_destroy = true + + encryption = { + default_kms_key_name = module.artifact_registry_kms.keys[var.key_name] + } + + depends_on = [module.artifact_registry_kms] +} diff --git a/modules/secure-cloud-serverless-harness/network.tf b/modules/secure-serverless-harness/network.tf similarity index 68% rename from modules/secure-cloud-serverless-harness/network.tf rename to modules/secure-serverless-harness/network.tf index 506d8414..4cffa574 100644 --- a/modules/secure-cloud-serverless-harness/network.tf +++ b/modules/secure-serverless-harness/network.tf @@ -16,14 +16,18 @@ locals { network_name = trimprefix(var.vpc_name, "vpc-") == var.vpc_name ? var.vpc_name : "vpc-${var.vpc_name}" + + services_projects = var.use_shared_vpc ? { for key, project in module.serverless_project : key => project.project_id } : {} + network_projects = var.use_shared_vpc ? { for key, project in module.network_project : key => project.project_id } : { for key, project in module.serverless_project : key => project.project_id } } module "network" { + for_each = local.network_projects source = "terraform-google-modules/network/google" - version = "~> 6.0" - project_id = module.serverless_project.project_id + version = "~> 7.0" + project_id = each.value network_name = local.network_name - shared_vpc_host = "false" + shared_vpc_host = var.use_shared_vpc delete_default_internet_gateway_routes = "true" subnets = [ @@ -68,18 +72,28 @@ module "network" { ports = ["443"] }] - ranges = [module.private_service_connect.private_service_connect_ip] + ranges = [var.private_service_connect_ip] target_tags = ["allow-google-apis", "vpc-connector"] } ] } +resource "google_compute_shared_vpc_service_project" "shared_vpc_attachment" { + for_each = local.services_projects + + host_project = module.network[0].project_id + service_project = each.value + depends_on = [module.serverless_project] +} + resource "google_dns_policy" "default_policy" { - project = module.serverless_project.project_id + for_each = module.network + + project = each.value.project_id name = "dns-default-policy" enable_inbound_forwarding = var.dns_enable_inbound_forwarding enable_logging = var.dns_enable_logging networks { - network_url = module.network.network_self_link + network_url = each.value.network_self_link } } diff --git a/modules/secure-cloud-serverless-harness/outputs.tf b/modules/secure-serverless-harness/outputs.tf similarity index 65% rename from modules/secure-cloud-serverless-harness/outputs.tf rename to modules/secure-serverless-harness/outputs.tf index 28d300c9..251be85c 100644 --- a/modules/secure-cloud-serverless-harness/outputs.tf +++ b/modules/secure-serverless-harness/outputs.tf @@ -23,18 +23,27 @@ output "serverless_folder_id" { ] } -output "serverless_project_id" { - value = module.serverless_project.project_id - description = "Project ID of the project created to deploy Cloud Run." +output "network_project_id" { + value = [for network in module.network : network.project_id] + description = "Project ID of the project created to host the Cloud Run Network." depends_on = [ time_sleep.wait_90_seconds ] } -output "serverless_project_number" { - value = module.serverless_project.project_number - description = "Project number of the project created to deploy Cloud Run." +output "serverless_project_ids" { + value = [for project in module.serverless_project : project.project_id] + description = "Project ID of the projects created to deploy Cloud Run." + + depends_on = [ + time_sleep.wait_90_seconds + ] +} + +output "serverless_project_numbers" { + value = { for project in module.serverless_project : project.project_id => project.project_number } + description = "Project number of the projects created to deploy Cloud Run." depends_on = [ time_sleep.wait_90_seconds @@ -60,8 +69,8 @@ output "security_project_number" { } output "service_account_email" { - value = module.service_accounts.email - description = "The email of the Service Account created to be used by Cloud Run." + value = { for project in module.serverless_project : project.project_id => project.service_account_email } + description = "The email of the Service Account created to be used by Cloud Serverless." depends_on = [ time_sleep.wait_90_seconds @@ -69,8 +78,8 @@ output "service_account_email" { } output "service_vpc" { - value = module.network.network - description = "The network created for Cloud Run." + value = [for network in module.network : network.network] + description = "The network created for Cloud Serverless." depends_on = [ time_sleep.wait_90_seconds @@ -78,7 +87,7 @@ output "service_vpc" { } output "service_subnet" { - value = module.network.subnets_names[0] + value = [for network in module.network : network.subnets_names[0]] description = "The sub-network name created in harness." depends_on = [ @@ -104,8 +113,8 @@ output "artifact_registry_repository_name" { ] } -output "cloud_run_service_identity_email" { - value = google_project_service_identity.serverless_sa.email +output "cloud_serverless_service_identity_email" { + value = { for project in module.serverless_project : project.project_id => project.cloud_serverless_service_identity_email } description = "The Cloud Run Service Identity email." depends_on = [ @@ -130,3 +139,12 @@ output "restricted_access_level_name" { time_sleep.wait_90_seconds ] } + +output "cloudfunction_source_bucket" { + value = var.serverless_type == "CLOUD_RUN" ? {} : { for bucket in module.cloudfunction_source_bucket : bucket.bucket.project => bucket.bucket } + description = "Cloud Function Source Bucket." + + depends_on = [ + time_sleep.wait_90_seconds + ] +} diff --git a/modules/secure-cloud-serverless-harness/private_service_connect.tf b/modules/secure-serverless-harness/private_service_connect.tf similarity index 86% rename from modules/secure-cloud-serverless-harness/private_service_connect.tf rename to modules/secure-serverless-harness/private_service_connect.tf index 7a42a8fe..aaeb07d6 100644 --- a/modules/secure-cloud-serverless-harness/private_service_connect.tf +++ b/modules/secure-serverless-harness/private_service_connect.tf @@ -15,10 +15,12 @@ */ module "private_service_connect" { + for_each = module.network + source = "terraform-google-modules/network/google//modules/private-service-connect" version = "~> 6.0" - project_id = module.serverless_project.project_id - network_self_link = module.network.network_self_link + project_id = each.value.project_id + network_self_link = each.value.network_self_link private_service_connect_ip = var.private_service_connect_ip forwarding_rule_target = "vpc-sc" } diff --git a/modules/secure-cloud-serverless-harness/service_perimeter.tf b/modules/secure-serverless-harness/service_perimeter.tf similarity index 85% rename from modules/secure-cloud-serverless-harness/service_perimeter.tf rename to modules/secure-serverless-harness/service_perimeter.tf index e0e0c439..916840f6 100644 --- a/modules/secure-cloud-serverless-harness/service_perimeter.tf +++ b/modules/secure-serverless-harness/service_perimeter.tf @@ -19,7 +19,10 @@ locals { access_level_name = "alp_${local.prefix}_members_${random_id.random_access_level_suffix.hex}" perimeter_name = "sp_${local.prefix}_perimeter_${random_id.random_access_level_suffix.hex}" 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 - + access_level_members = concat(var.access_level_members, + [for project in module.serverless_project : "serviceAccount:${project.services_identities["cloudbuild"]}"], + [for project in module.serverless_project : "serviceAccount:${project.services_identities["gcs"]}"] + ) } resource "random_id" "random_access_level_suffix" { @@ -42,7 +45,7 @@ module "access_level_members" { description = "${local.prefix} Access Level" policy = local.access_context_manager_policy_id name = local.access_level_name - members = var.access_level_members + members = local.access_level_members } module "regular_service_perimeter" { @@ -183,19 +186,42 @@ module "regular_service_perimeter" { } resource "google_access_context_manager_service_perimeter_resource" "service_perimeter_serverless_resource" { + for_each = module.serverless_project perimeter_name = "accessPolicies/${local.access_context_manager_policy_id}/servicePerimeters/${module.regular_service_perimeter.perimeter_name}" - resource = "projects/${module.serverless_project.project_number}" + resource = "projects/${each.value.project_number}" + depends_on = [ + module.serverless_project, + module.security_project, + module.network_project + ] } resource "google_access_context_manager_service_perimeter_resource" "service_perimeter_security_resource" { perimeter_name = "accessPolicies/${local.access_context_manager_policy_id}/servicePerimeters/${module.regular_service_perimeter.perimeter_name}" resource = "projects/${module.security_project.project_number}" + depends_on = [ + module.serverless_project, + module.security_project, + module.network_project + ] +} + +resource "google_access_context_manager_service_perimeter_resource" "service_perimeter_network_resource" { + count = var.use_shared_vpc ? 1 : 0 + perimeter_name = "accessPolicies/${local.access_context_manager_policy_id}/servicePerimeters/${module.regular_service_perimeter.perimeter_name}" + resource = "projects/${module.network_project[0].project_number}" + depends_on = [ + module.serverless_project, + module.security_project, + module.network_project + ] } resource "time_sleep" "wait_90_seconds" { depends_on = [ google_access_context_manager_service_perimeter_resource.service_perimeter_security_resource, - google_access_context_manager_service_perimeter_resource.service_perimeter_serverless_resource + google_access_context_manager_service_perimeter_resource.service_perimeter_serverless_resource, + google_access_context_manager_service_perimeter_resource.service_perimeter_network_resource ] create_duration = "90s" diff --git a/modules/secure-cloud-serverless-harness/variables.tf b/modules/secure-serverless-harness/variables.tf similarity index 92% rename from modules/secure-cloud-serverless-harness/variables.tf rename to modules/secure-serverless-harness/variables.tf index fcf993f8..68e01f12 100644 --- a/modules/secure-cloud-serverless-harness/variables.tf +++ b/modules/secure-serverless-harness/variables.tf @@ -29,9 +29,15 @@ variable "security_project_name" { type = string } -variable "serverless_project_name" { - description = "The name to give the Cloud Run project." +variable "network_project_name" { + description = "The name to give the shared vpc project." type = string + default = "" +} + +variable "serverless_project_names" { + description = "The name to give the Cloud Serverless project." + type = list(string) } variable "org_id" { @@ -63,6 +69,12 @@ variable "create_access_context_manager_access_policy" { default = false } +variable "use_shared_vpc" { + description = "Defines if the network created will be a single or shared vpc." + type = bool + default = false +} + variable "access_level_members" { description = "The list of additional members who will be in the access level." type = list(string) @@ -107,9 +119,9 @@ variable "private_service_connect_ip" { } variable "service_account_project_roles" { - type = list(string) - description = "Common roles to apply to the Cloud Run service account in the serverless project." - default = [] + type = map(list(string)) + description = "Common roles to apply to the Cloud Serverless service account in the serverless project." + default = {} } variable "artifact_registry_repository_name" { diff --git a/modules/secure-cloud-serverless-harness/versions.tf b/modules/secure-serverless-harness/versions.tf similarity index 94% rename from modules/secure-cloud-serverless-harness/versions.tf rename to modules/secure-serverless-harness/versions.tf index 71d5fc8c..64901496 100644 --- a/modules/secure-cloud-serverless-harness/versions.tf +++ b/modules/secure-serverless-harness/versions.tf @@ -37,10 +37,10 @@ terraform { } provider_meta "google" { - module_name = "blueprints/terraform/terraform-google-cloud-run:secure-cloud-harness/v0.6.0" + module_name = "blueprints/terraform/terraform-google-cloud-run:secure-cloud-harness/v0.5.0" } provider_meta "google-beta" { - module_name = "blueprints/terraform/terraform-google-cloud-run:secure-cloud-harness/v0.6.0" + module_name = "blueprints/terraform/terraform-google-cloud-run:secure-cloud-harness/v0.5.0" } } diff --git a/modules/secure-cloud-serverless-net/README.md b/modules/secure-serverless-net/README.md similarity index 97% rename from modules/secure-cloud-serverless-net/README.md rename to modules/secure-serverless-net/README.md index 568bb679..65bed947 100644 --- a/modules/secure-cloud-serverless-net/README.md +++ b/modules/secure-serverless-net/README.md @@ -50,6 +50,7 @@ module "cloud_run_network" { | resource\_names\_suffix | A suffix to concat in the end of the resources names. | `string` | `null` | no | | serverless\_project\_id | The project where cloud run is going to be deployed. | `string` | n/a | yes | | serverless\_service\_identity\_email | The Service Identity email for the serverless resource (Cloud Run or Cloud Function). | `string` | n/a | yes | +| serverless\_type | The type of resource to be used. It supports only CLOUD\_RUN or CLOUD\_FUNCTION | `string` | n/a | yes | | shared\_vpc\_name | Shared VPC name which is going to be used to create Serverless Connector. | `string` | n/a | yes | | subnet\_name | Subnet name to be re-used to create Serverless Connector. | `string` | n/a | yes | | vpc\_project\_id | The project where shared vpc is. | `string` | n/a | yes | diff --git a/modules/secure-cloud-serverless-net/firewall.tf b/modules/secure-serverless-net/firewall.tf similarity index 100% rename from modules/secure-cloud-serverless-net/firewall.tf rename to modules/secure-serverless-net/firewall.tf diff --git a/modules/secure-cloud-serverless-net/iam.tf b/modules/secure-serverless-net/iam.tf similarity index 95% rename from modules/secure-cloud-serverless-net/iam.tf rename to modules/secure-serverless-net/iam.tf index 43e787a7..ffcf70ee 100644 --- a/modules/secure-cloud-serverless-net/iam.tf +++ b/modules/secure-serverless-net/iam.tf @@ -14,6 +14,10 @@ * limitations under the License. */ +locals { + api = var.serverless_type == "CLOUD_RUN" ? "run" : "cloudfunctions" +} + data "google_project" "serverless_project_id" { project_id = var.serverless_project_id } diff --git a/modules/secure-cloud-serverless-net/network.tf b/modules/secure-serverless-net/network.tf similarity index 100% rename from modules/secure-cloud-serverless-net/network.tf rename to modules/secure-serverless-net/network.tf diff --git a/modules/secure-cloud-serverless-net/outputs.tf b/modules/secure-serverless-net/outputs.tf similarity index 100% rename from modules/secure-cloud-serverless-net/outputs.tf rename to modules/secure-serverless-net/outputs.tf diff --git a/modules/secure-cloud-serverless-net/variables.tf b/modules/secure-serverless-net/variables.tf similarity index 94% rename from modules/secure-cloud-serverless-net/variables.tf rename to modules/secure-serverless-net/variables.tf index f7c9dd35..794c7a8c 100644 --- a/modules/secure-cloud-serverless-net/variables.tf +++ b/modules/secure-serverless-net/variables.tf @@ -77,3 +77,8 @@ variable "resource_names_suffix" { type = string default = null } + +variable "serverless_type" { + description = "The type of resource to be used. It supports only CLOUD_RUN or CLOUD_FUNCTION" + type = string +} diff --git a/modules/secure-cloud-serverless-net/versions.tf b/modules/secure-serverless-net/versions.tf similarity index 100% rename from modules/secure-cloud-serverless-net/versions.tf rename to modules/secure-serverless-net/versions.tf diff --git a/modules/service-project-factory/main.tf b/modules/service-project-factory/main.tf new file mode 100644 index 00000000..a5ad6c8c --- /dev/null +++ b/modules/service-project-factory/main.tf @@ -0,0 +1,91 @@ +/** + * 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 { + api = var.serverless_type == "CLOUD_RUN" ? "run" : "cloudfunctions" +} +module "serverless_project" { + source = "terraform-google-modules/project-factory/google" + version = "~> 14.2" + random_project_id = "true" + activate_apis = var.activate_apis + name = var.project_name + org_id = var.org_id + billing_account = var.billing_account + folder_id = var.folder_name + + svpc_host_project_id = var.network_project_id + grant_network_role = var.network_project_id != "" ? true : false +} + +module "service_accounts" { + source = "terraform-google-modules/service-accounts/google" + version = "~> 4.2" + project_id = module.serverless_project.project_id + prefix = "sa" + names = [replace(lower(var.serverless_type), "_", "-")] +} + +resource "google_project_iam_member" "cloud_run_sa_roles" { + for_each = toset(var.service_account_project_roles) + project = module.serverless_project.project_id + role = each.value + member = module.service_accounts.iam_email +} + +resource "google_project_service_identity" "serverless_sa" { + provider = google-beta + + project = module.serverless_project.project_id + service = "${local.api}.googleapis.com" +} + +resource "google_service_account_iam_member" "identity_service_account_user" { + service_account_id = module.service_accounts.service_account.id + role = "roles/iam.serviceAccountUser" + member = "serviceAccount:${google_project_service_identity.serverless_sa.email}" +} +resource "google_project_service_identity" "cloudbuild_sa" { + provider = google-beta + + project = module.serverless_project.project_id + service = "cloudbuild.googleapis.com" +} + +resource "google_project_service_identity" "eventarc_sa" { + provider = google-beta + + project = module.serverless_project.project_id + service = "eventarc.googleapis.com" +} + +data "google_storage_project_service_account" "gcs_account" { + project = module.serverless_project.project_id +} + +resource "google_project_iam_member" "gcs_pubsub_publishing" { + count = var.serverless_type == "CLOUD_RUN" ? 0 : 1 + project = module.serverless_project.project_id + role = "roles/pubsub.publisher" + member = "serviceAccount:${data.google_storage_project_service_account.gcs_account.email_address}" +} + +resource "google_project_iam_member" "eventarc_service_agent" { + project = module.serverless_project.project_id + role = "roles/eventarc.serviceAgent" + member = "serviceAccount:${google_project_service_identity.eventarc_sa.email}" +} diff --git a/modules/service-project-factory/outputs.tf b/modules/service-project-factory/outputs.tf new file mode 100644 index 00000000..08681e7a --- /dev/null +++ b/modules/service-project-factory/outputs.tf @@ -0,0 +1,45 @@ +/** + * 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. + */ + +output "project_id" { + value = module.serverless_project.project_id + description = "Project ID of the project created to deploy Cloud Serverless." +} + +output "project_number" { + value = module.serverless_project.project_number + description = "Project number of the project created to deploy Cloud Serverless." +} + +output "service_account_email" { + value = module.service_accounts.email + description = "The service account created tin the project." +} + +output "cloud_serverless_service_identity_email" { + value = google_project_service_identity.serverless_sa.email + description = "The Cloud Serverless Service Identity email." +} + +output "services_identities" { + value = { + "eventarc" = google_project_service_identity.eventarc_sa.email, + "cloudbuild" = google_project_service_identity.cloudbuild_sa.email, + "gcs" = data.google_storage_project_service_account.gcs_account.email_address, + "serverless" = google_project_service_identity.serverless_sa.email + } + description = "Services Identities for the serverless project." +} diff --git a/modules/service-project-factory/variables.tf b/modules/service-project-factory/variables.tf new file mode 100644 index 00000000..249049c1 --- /dev/null +++ b/modules/service-project-factory/variables.tf @@ -0,0 +1,57 @@ +/** + * 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. + */ + +variable "billing_account" { + description = "The ID of the billing account to associate this project with." + type = string +} + +variable "serverless_type" { + description = "The type of resource to be used. It supports only CLOUD_RUN or CLOUD_FUNCTION" + type = string +} + +variable "network_project_id" { + description = "The network project_id when using Shared VPC." + type = string + default = "" +} + +variable "project_name" { + description = "The name to give the Cloud Serverless project." + type = string +} + +variable "org_id" { + description = "The organization ID." + type = string +} + +variable "activate_apis" { + description = "The APIs to enabled when creating the project." + type = list(string) +} + +variable "folder_name" { + description = "The folder name." + type = string +} + +variable "service_account_project_roles" { + type = list(string) + description = "Common roles to apply to the Cloud Run service account in the serverless project." + default = [] +}