diff --git a/4-projects/business_unit_3/shared/README.md b/4-projects/business_unit_3/shared/README.md
index cfcdd22c..b65cb8a9 100644
--- a/4-projects/business_unit_3/shared/README.md
+++ b/4-projects/business_unit_3/shared/README.md
@@ -11,6 +11,7 @@
| keyring\_name | Name to be used for KMS Keyring | `string` | `"sample-keyring"` | no |
| location\_gcs | Case-Sensitive Location for GCS Bucket | `string` | `"US"` | no |
| location\_kms | Case-Sensitive Location for KMS Keyring | `string` | `"us"` | no |
+| prevent\_destroy | Prevent Project Key destruction. | `bool` | `true` | no |
| project\_budget | Budget configuration.
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({| `{}` | 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 | diff --git a/4-projects/business_unit_3/shared/ml_infra_projects.tf b/4-projects/business_unit_3/shared/ml_infra_projects.tf new file mode 100644 index 00000000..acc9abea --- /dev/null +++ b/4-projects/business_unit_3/shared/ml_infra_projects.tf @@ -0,0 +1,36 @@ +/** + * Copyright 2024 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. + */ + +module "ml_infra_project" { + source = "../../modules/ml_infra_projects" + + org_id = local.org_id + folder_id = local.common_folder_name + billing_account = local.billing_account + environment = "common" + key_rings = local.shared_kms_key_ring + business_code = "bu3" + billing_code = "1234" + primary_contact = "example@example.com" + secondary_contact = "example2@example.com" + cloud_source_artifacts_repo_name = var.cloud_source_artifacts_repo_name + cloud_source_service_catalog_repo_name = var.cloud_source_service_catalog_repo_name + remote_state_bucket = var.remote_state_bucket + artifacts_infra_pipeline_sa = module.infra_pipelines[0].terraform_service_accounts["bu3-artifact-publish"] + service_catalog_infra_pipeline_sa = module.infra_pipelines[0].terraform_service_accounts["bu3-service-catalog"] + environment_kms_project_id = "" + prevent_destroy = var.prevent_destroy +} diff --git a/4-projects/business_unit_3/shared/outputs.tf b/4-projects/business_unit_3/shared/outputs.tf index f4dfe92d..2d0e696d 100644 --- a/4-projects/business_unit_3/shared/outputs.tf +++ b/4-projects/business_unit_3/shared/outputs.tf @@ -65,30 +65,30 @@ output "enable_cloudbuild_deploy" { output "service_catalog_project_id" { description = "Service Catalog Project ID." - value = try(module.app_service_catalog_project[0].project_id, "") + value = module.ml_infra_project.service_catalog_project_id } output "common_artifacts_project_id" { description = "App Infra Artifacts Project ID" - value = try(module.app_infra_artifacts_project[0].project_id, "") + value = module.ml_infra_project.common_artifacts_project_id } output "service_catalog_repo_name" { description = "The name of the Service Catalog repository" - value = google_sourcerepo_repository.service_catalog.name + value = module.ml_infra_project.service_catalog_repo_name } output "service_catalog_repo_id" { description = "ID of the Service Catalog repository" - value = google_sourcerepo_repository.service_catalog.id + value = module.ml_infra_project.service_catalog_repo_id } output "artifacts_repo_name" { description = "The name of the Artifacts repository" - value = google_sourcerepo_repository.artifact_repo.name + value = module.ml_infra_project.artifacts_repo_name } output "artifacts_repo_id" { description = "ID of the Artifacts repository" - value = google_sourcerepo_repository.artifact_repo.id + value = module.ml_infra_project.artifacts_repo_id } diff --git a/4-projects/business_unit_3/shared/variables.tf b/4-projects/business_unit_3/shared/variables.tf index d06456fd..7efc60d6 100644 --- a/4-projects/business_unit_3/shared/variables.tf +++ b/4-projects/business_unit_3/shared/variables.tf @@ -87,3 +87,9 @@ variable "cloud_source_artifacts_repo_name" { description = "Name to give the could source repository for Artifacts" type = string } + +variable "prevent_destroy" { + description = "Prevent Project Key destruction." + type = bool + default = true +} diff --git a/4-projects/business_unit_3/shared/example_artifacts.tf b/4-projects/modules/ml_infra_projects/artifacts_project.tf similarity index 57% rename from 4-projects/business_unit_3/shared/example_artifacts.tf rename to 4-projects/modules/ml_infra_projects/artifacts_project.tf index a24433bc..e3260633 100644 --- a/4-projects/business_unit_3/shared/example_artifacts.tf +++ b/4-projects/modules/ml_infra_projects/artifacts_project.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,16 +27,15 @@ locals { } module "app_infra_artifacts_project" { - source = "../../modules/single_project" - # count = local.enable_cloudbuild_deploy ? 1 : 0 + source = "../ml_single_project" - org_id = local.org_id - billing_account = local.billing_account - folder_id = local.common_folder_name - environment = "common" + org_id = var.org_id + billing_account = var.billing_account + folder_id = var.folder_id + environment = var.environment project_budget = var.project_budget - project_prefix = local.project_prefix - key_rings = local.shared_kms_key_ring + project_prefix = var.project_prefix + key_rings = var.key_rings remote_state_bucket = var.remote_state_bucket activate_apis = [ "artifactregistry.googleapis.com", @@ -49,48 +48,40 @@ module "app_infra_artifacts_project" { "sourcerepo.googleapis.com", ] # Metadata - project_suffix = "artifacts" - application_name = "app-infra-artifacts" - billing_code = "1234" - primary_contact = "example@example.com" - secondary_contact = "example2@example.com" - business_code = "bu3" + project_suffix = var.artifacts_project_suffix + application_name = var.artifacts_application_name + billing_code = var.billing_code + primary_contact = var.primary_contact + secondary_contact = var.secondary_contact + business_code = var.business_code + environment_kms_project_id = var.environment_kms_project_id + project_name = "${var.project_prefix}-${local.env_code}-${var.business_code}${var.artifacts_project_suffix}" + prevent_destroy = var.prevent_destroy } -# resource "google_kms_crypto_key" "ml_key" { -# for_each = toset(local.shared_kms_key_ring) -# name = module.app_infra_artifacts_project[0].project_name -# key_ring = each.key -# rotation_period = var.key_rotation_period -# lifecycle { -# prevent_destroy = false -# } -# } - resource "google_kms_crypto_key_iam_member" "ml_key" { - for_each = module.app_infra_cloudbuild_project[0].kms_keys + for_each = module.app_infra_artifacts_project.kms_keys crypto_key_id = each.value.id role = "roles/cloudkms.admin" - member = "serviceAccount:${module.infra_pipelines[0].terraform_service_accounts["bu3-artifact-publish"]}" + member = "serviceAccount:${var.artifacts_infra_pipeline_sa}" } resource "google_project_iam_member" "artifact_tf_sa_roles" { for_each = toset(local.artifact_tf_sa_roles) - project = module.app_infra_artifacts_project[0].project_id + project = module.app_infra_artifacts_project.project_id role = each.key - member = "serviceAccount:${module.infra_pipelines[0].terraform_service_accounts["bu3-artifact-publish"]}" + member = "serviceAccount:${var.artifacts_infra_pipeline_sa}" } // Add Service Agent for Cloud Build resource "google_project_iam_member" "artifact_cloudbuild_agent" { - project = module.app_infra_artifacts_project[0].project_id + project = module.app_infra_artifacts_project.project_id role = "roles/secretmanager.secretAccessor" - member = "serviceAccount:${module.app_infra_artifacts_project[0].project_number}@cloudbuild.gserviceaccount.com" + member = "serviceAccount:${module.app_infra_artifacts_project.project_number}@cloudbuild.gserviceaccount.com" } // Add Repository for Artifact repo - resource "google_sourcerepo_repository" "artifact_repo" { - project = module.app_infra_artifacts_project[0].project_id + project = module.app_infra_artifacts_project.project_id name = var.cloud_source_artifacts_repo_name } diff --git a/4-projects/modules/ml_infra_projects/locals.tf b/4-projects/modules/ml_infra_projects/locals.tf new file mode 100644 index 00000000..32210121 --- /dev/null +++ b/4-projects/modules/ml_infra_projects/locals.tf @@ -0,0 +1,19 @@ +/** + * Copyright 2024 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 { + env_code = element(split("", var.environment), 0) +} diff --git a/4-projects/modules/ml_infra_projects/outputs.tf b/4-projects/modules/ml_infra_projects/outputs.tf new file mode 100644 index 00000000..3b73df7c --- /dev/null +++ b/4-projects/modules/ml_infra_projects/outputs.tf @@ -0,0 +1,45 @@ +/** + * Copyright 2024 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 "service_catalog_project_id" { + description = "Service Catalog Project ID." + value = try(module.app_service_catalog_project.project_id, "") +} + +output "common_artifacts_project_id" { + description = "App Infra Artifacts Project ID." + value = try(module.app_infra_artifacts_project.project_id, "") +} + +output "service_catalog_repo_name" { + description = "The name of the Service Catalog repository." + value = google_sourcerepo_repository.service_catalog.name +} + +output "service_catalog_repo_id" { + description = "ID of the Service Catalog repository." + value = google_sourcerepo_repository.service_catalog.id +} + +output "artifacts_repo_name" { + description = "The name of the Artifacts repository." + value = google_sourcerepo_repository.artifact_repo.name +} + +output "artifacts_repo_id" { + description = "ID of the Artifacts repository." + value = google_sourcerepo_repository.artifact_repo.id +} diff --git a/4-projects/business_unit_3/shared/example_service_catalog.tf b/4-projects/modules/ml_infra_projects/service_catalog_project.tf similarity index 59% rename from 4-projects/business_unit_3/shared/example_service_catalog.tf rename to 4-projects/modules/ml_infra_projects/service_catalog_project.tf index 46cae7ab..7e0a19b3 100644 --- a/4-projects/business_unit_3/shared/example_service_catalog.tf +++ b/4-projects/modules/ml_infra_projects/service_catalog_project.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,16 +26,15 @@ locals { } module "app_service_catalog_project" { - source = "../../modules/single_project" - # count = local.enable_cloudbuild_deploy ? 1 : 0 + source = "../ml_single_project" - org_id = local.org_id - billing_account = local.billing_account - folder_id = local.common_folder_name - environment = "common" + org_id = var.org_id + billing_account = var.billing_account + folder_id = var.folder_id + environment = var.environment project_budget = var.project_budget - project_prefix = local.project_prefix - key_rings = local.shared_kms_key_ring + project_prefix = var.project_prefix + key_rings = var.key_rings remote_state_bucket = var.remote_state_bucket activate_apis = [ "logging.googleapis.com", @@ -47,31 +46,34 @@ module "app_service_catalog_project" { "sourcerepo.googleapis.com", ] # Metadata - project_suffix = var.cloud_source_service_catalog_repo_name - application_name = "app-infra-ml" - billing_code = "1234" - primary_contact = "example@example.com" - secondary_contact = "example2@example.com" - business_code = "bu3" + project_suffix = var.service_catalog_project_suffix + application_name = var.service_catalog_application_name + billing_code = var.billing_code + primary_contact = var.primary_contact + secondary_contact = var.secondary_contact + business_code = var.business_code + environment_kms_project_id = var.environment_kms_project_id + project_name = "${var.project_prefix}-${local.env_code}-${var.business_code}${var.service_catalog_project_suffix}" + prevent_destroy = var.prevent_destroy } resource "google_kms_crypto_key_iam_member" "sc_key" { - for_each = module.app_service_catalog_project[0].kms_keys + for_each = module.app_service_catalog_project.kms_keys crypto_key_id = each.value.id role = "roles/cloudkms.admin" - member = "serviceAccount:${module.infra_pipelines[0].terraform_service_accounts["bu3-service-catalog"]}" + member = "serviceAccount:${var.service_catalog_infra_pipeline_sa}" } // Grab Service Agent for Secret Manager resource "google_project_service_identity" "secretmanager_agent" { provider = google-beta - project = module.app_service_catalog_project[0].project_id + project = module.app_service_catalog_project.project_id service = "secretmanager.googleapis.com" } // Add Secret Manager Service Agent to key with encrypt/decrypt permissions resource "google_kms_crypto_key_iam_member" "secretmanager_agent" { - for_each = module.app_service_catalog_project[0].kms_keys + for_each = module.app_service_catalog_project.kms_keys crypto_key_id = each.value.id role = "roles/cloudkms.cryptoKeyEncrypterDecrypter" member = "serviceAccount:${google_project_service_identity.secretmanager_agent.email}" @@ -80,55 +82,43 @@ resource "google_kms_crypto_key_iam_member" "secretmanager_agent" { // Grab Service Agent for Storage resource "google_project_service_identity" "storage" { provider = google-beta - project = module.app_service_catalog_project[0].project_id + project = module.app_service_catalog_project.project_id service = "storage.googleapis.com" } // Add Service Agent for Storage resource "google_kms_crypto_key_iam_member" "storage_agent" { - for_each = module.app_service_catalog_project[0].kms_keys + for_each = module.app_service_catalog_project.kms_keys crypto_key_id = each.value.id role = "roles/cloudkms.cryptoKeyEncrypterDecrypter" - member = "serviceAccount:service-${module.app_service_catalog_project[0].project_number}@gs-project-accounts.iam.gserviceaccount.com" + member = "serviceAccount:service-${module.app_service_catalog_project.project_number}@gs-project-accounts.iam.gserviceaccount.com" depends_on = [google_project_service_identity.storage] } // Add infra pipeline SA encrypt/decrypt permissions resource "google_kms_crypto_key_iam_member" "storage-kms-key-binding" { - for_each = module.app_service_catalog_project[0].kms_keys + for_each = module.app_service_catalog_project.kms_keys crypto_key_id = each.value.id role = "roles/cloudkms.cryptoKeyEncrypterDecrypter" - member = "serviceAccount:${module.infra_pipelines[0].terraform_service_accounts["bu3-service-catalog"]}" + member = "serviceAccount:${var.service_catalog_infra_pipeline_sa}" } resource "google_project_iam_member" "service_catalog_tf_sa_roles" { for_each = toset(local.service_catalog_tf_sa_roles) - project = module.app_service_catalog_project[0].project_id + project = module.app_service_catalog_project.project_id role = each.key - member = "serviceAccount:${module.infra_pipelines[0].terraform_service_accounts["bu3-service-catalog"]}" + member = "serviceAccount:${var.service_catalog_infra_pipeline_sa}" } // Add Service Agent for Cloud Build resource "google_project_iam_member" "cloudbuild_agent" { - project = module.app_service_catalog_project[0].project_id + project = module.app_service_catalog_project.project_id role = "roles/secretmanager.secretAccessor" - member = "serviceAccount:${module.app_service_catalog_project[0].project_number}@cloudbuild.gserviceaccount.com" + member = "serviceAccount:${module.app_service_catalog_project.project_number}@cloudbuild.gserviceaccount.com" } // Add Service Catalog Source Repository - resource "google_sourcerepo_repository" "service_catalog" { - project = module.app_service_catalog_project[0].project_id + project = module.app_service_catalog_project.project_id name = var.cloud_source_service_catalog_repo_name } - -/** - * When Jenkins CICD is used for deployment this resource - * is created to terraform validation works. - * Without this resource, this module creates zero resources - * and it breaks terraform validation throwing the error below: - * ERROR: [Terraform plan json does not contain resource_changes key] - */ -resource "null_resource" "jenkins_cicd_service_catalog" { - count = !local.enable_cloudbuild_deploy ? 1 : 0 -} diff --git a/4-projects/modules/ml_infra_projects/variables.tf b/4-projects/modules/ml_infra_projects/variables.tf new file mode 100644 index 00000000..e9f891a9 --- /dev/null +++ b/4-projects/modules/ml_infra_projects/variables.tf @@ -0,0 +1,203 @@ +/** + * Copyright 2024 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 "org_id" { + description = "The Organization ID." + type = string +} + +variable "billing_account" { + description = "The ID of the billing account to associated this project with." + type = string +} + +variable "folder_id" { + description = "The folder id where project will be created." + type = string +} + +variable "environment" { + description = "The environment the single project belongs to." + type = string +} + +variable "project_budget" { + description = <
budget_amount = optional(number, 1000)
alert_spent_percents = optional(list(number), [1.2])
alert_pubsub_topic = optional(string, null)
alert_spend_basis = optional(string, "FORECASTED_SPEND")
})
object({| `{}` | no | +| project\_name | Project Name. | `string` | n/a | yes | +| project\_prefix | Name prefix to use for projects created. | `string` | `"prj"` | no | +| project\_suffix | The name of the GCP project. Max 16 characters with 3 character business unit code. | `string` | n/a | yes | +| remote\_state\_bucket | Backend bucket to load Terraform Remote State Data from previous steps. | `string` | n/a | yes | +| sa\_roles | A list of roles to give the Service Account from App Infra Pipeline. | `map(list(string))` | `{}` | no | +| secondary\_contact | The secondary email contact for the project. | `string` | `""` | no | +| shared\_vpc\_host\_project\_id | Shared VPC host project ID. | `string` | `""` | no | +| shared\_vpc\_subnets | List of the shared vpc subnets self links. | `list(string)` | `[]` | no | +| vpc\_service\_control\_attach\_enabled | Whether the project will be attached to a VPC Service Control Perimeter. | `bool` | `false` | no | +| vpc\_service\_control\_perimeter\_name | The name of a VPC Service Control Perimeter to add the created project to. | `string` | `null` | no | +| vpc\_service\_control\_sleep\_duration | The duration to sleep in seconds before adding the project to a shared VPC after the project is added to the VPC Service Control Perimeter. | `string` | `"5s"` | no | +| vpc\_type | The type of VPC to attach the project to. Possible options are `base` or `restricted`. | `string` | `""` | no | + +## Outputs + +| Name | Description | +|------|-------------| +| enabled\_apis | VPC Service Control services. | +| kms\_keys | Keys created for the project. | +| project\_id | Project ID. | +| project\_name | Project Name. | +| project\_number | Project number. | +| sa | Project SA email. | + + diff --git a/4-projects/modules/ml_single_project/main.tf b/4-projects/modules/ml_single_project/main.tf new file mode 100644 index 00000000..50afa4b1 --- /dev/null +++ b/4-projects/modules/ml_single_project/main.tf @@ -0,0 +1,141 @@ +/** + * Copyright 2024 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 { + enabled_apis = distinct(concat(var.activate_apis, ["billingbudgets.googleapis.com"])) + env_code = element(split("", var.environment), 0) + source_repos = setintersection( + toset(keys(var.app_infra_pipeline_service_accounts)), + toset(keys(var.sa_roles)) + ) + pipeline_roles = var.enable_cloudbuild_deploy ? flatten([ + for repo in local.source_repos : [ + for role in var.sa_roles[repo] : + { + repo = repo + role = role + sa = var.app_infra_pipeline_service_accounts[repo] + } + ] + ]) : [] + + pipeline_kms_sas = var.enable_cloudbuild_deploy ? flatten([ + for repo in keys(var.sa_roles) : [ + var.app_infra_pipeline_service_accounts[repo] + ] + ]) : [] + + network_user_role = var.enable_cloudbuild_deploy ? flatten([ + for repo in local.source_repos : [ + for subnet in var.shared_vpc_subnets : + { + repo = repo + subnet = element(split("/", subnet), index(split("/", subnet), "subnetworks", ) + 1, ) + region = element(split("/", subnet), index(split("/", subnet), "regions") + 1, ) + sa = var.app_infra_pipeline_service_accounts[repo] + } + ] + ]) : [] +} + +module "project" { + source = "terraform-google-modules/project-factory/google" + version = "~> 14.1" + + random_project_id = true + random_project_id_length = 4 + activate_apis = local.enabled_apis + name = var.project_name + org_id = var.org_id + billing_account = var.billing_account + folder_id = var.folder_id + default_service_account = var.default_service_account + + svpc_host_project_id = var.shared_vpc_host_project_id + shared_vpc_subnets = var.shared_vpc_subnets # Optional: To enable subnetting, replace to "module.networking_project.subnetwork_self_link" + + vpc_service_control_attach_enabled = var.vpc_service_control_attach_enabled + vpc_service_control_perimeter_name = var.vpc_service_control_perimeter_name + vpc_service_control_sleep_duration = var.vpc_service_control_sleep_duration + + labels = { + environment = var.environment + application_name = var.application_name + billing_code = var.billing_code + primary_contact = element(split("@", var.primary_contact), 0) + secondary_contact = element(split("@", var.secondary_contact), 0) + business_code = var.business_code + env_code = local.env_code + vpc_type = var.vpc_type + } + budget_alert_pubsub_topic = var.project_budget.alert_pubsub_topic + budget_alert_spent_percents = var.project_budget.alert_spent_percents + budget_amount = var.project_budget.budget_amount + budget_alert_spend_basis = var.project_budget.alert_spend_basis +} + +# Additional roles to the App Infra Pipeline service account +resource "google_project_iam_member" "app_infra_pipeline_sa_roles" { + for_each = { for pr in local.pipeline_roles : "${pr.repo}-${pr.sa}-${pr.role}" => pr } + + project = module.project.project_id + role = each.value.role + member = "serviceAccount:${each.value.sa}" +} + +resource "google_folder_iam_member" "folder_network_viewer" { + for_each = var.app_infra_pipeline_service_accounts + + folder = var.folder_id + role = "roles/compute.networkViewer" + member = "serviceAccount:${each.value}" +} + +resource "google_project_iam_member" "shared_vpc_network_viewer" { + for_each = var.shared_vpc_host_project_id != "" ? toset(local.pipeline_kms_sas) : toset([]) + + project = var.shared_vpc_host_project_id + role = "roles/compute.networkViewer" + member = "serviceAccount:${each.key}" +} + +resource "google_compute_subnetwork_iam_member" "account_role_to_vpc_subnets" { + provider = google-beta + for_each = { for nr in local.network_user_role : "${nr.repo}-${nr.subnet}-${nr.sa}" => nr } + + subnetwork = each.value.subnet + role = "roles/compute.networkUser" + region = each.value.region + project = var.shared_vpc_host_project_id + member = "serviceAccount:${each.value.sa}" +} + +// Add key for project +module "kms_keys" { + source = "../ml_kms_key" + key_rings = var.key_rings + key_rotation_period = var.key_rotation_period + project_name = module.project.project_name + prevent_destroy = var.prevent_destroy +} + +// Add crypto key viewer role to kms environment project +resource "google_project_iam_member" "kms_viewer" { + for_each = var.environment != "common" ? toset(local.pipeline_kms_sas) : toset([]) + project = var.environment_kms_project_id + role = "roles/cloudkms.viewer" + member = "serviceAccount:${each.key}" +} diff --git a/4-projects/modules/ml_single_project/outputs.tf b/4-projects/modules/ml_single_project/outputs.tf new file mode 100644 index 00000000..b4aba602 --- /dev/null +++ b/4-projects/modules/ml_single_project/outputs.tf @@ -0,0 +1,45 @@ +/** + * Copyright 2024 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" { + description = "Project ID." + value = module.project.project_id +} + +output "sa" { + description = "Project SA email." + value = module.project.service_account_email +} + +output "project_number" { + description = "Project number." + value = module.project.project_number +} + +output "enabled_apis" { + description = "VPC Service Control services." + value = local.enabled_apis +} + +output "project_name" { + description = "Project Name." + value = module.project.project_name +} + +output "kms_keys" { + description = "Keys created for the project." + value = module.kms_keys.kms_keys +} diff --git a/4-projects/modules/ml_single_project/variables.tf b/4-projects/modules/ml_single_project/variables.tf new file mode 100644 index 00000000..d55b3647 --- /dev/null +++ b/4-projects/modules/ml_single_project/variables.tf @@ -0,0 +1,187 @@ +/** + * Copyright 2024 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 "org_id" { + description = "The Organization ID." + type = string +} + +variable "folder_id" { + description = "The folder id where project will be created." + type = string +} + +variable "billing_account" { + description = "The ID of the billing account to associated this project with." + type = string +} + +variable "project_suffix" { + description = "The name of the GCP project. Max 16 characters with 3 character business unit code." + type = string +} + +variable "application_name" { + description = "The name of application where GCP resources relate." + type = string +} + +variable "billing_code" { + description = "The code that's used to provide chargeback information." + type = string +} + +variable "primary_contact" { + description = "The primary email contact for the project." + type = string +} + +variable "secondary_contact" { + description = "The secondary email contact for the project." + type = string + default = "" +} + +variable "business_code" { + description = "The code that describes which business unit owns the project." + type = string + default = "abcd" +} + +variable "activate_apis" { + description = "The api to activate for the GCP project." + type = list(string) + default = [] +} + +variable "environment" { + description = "The environment the project belongs to." + type = string +} + +variable "vpc_type" { + description = "The type of VPC to attach the project to. Possible options are `base` or `restricted`." + type = string + default = "" +} + +variable "shared_vpc_host_project_id" { + description = "Shared VPC host project ID." + type = string + default = "" +} + +variable "shared_vpc_subnets" { + description = "List of the shared vpc subnets self links." + type = list(string) + default = [] +} + +variable "vpc_service_control_attach_enabled" { + description = "Whether the project will be attached to a VPC Service Control Perimeter." + type = bool + default = false +} + +variable "vpc_service_control_perimeter_name" { + description = "The name of a VPC Service Control Perimeter to add the created project to." + type = string + default = null +} + +variable "vpc_service_control_sleep_duration" { + description = "The duration to sleep in seconds before adding the project to a shared VPC after the project is added to the VPC Service Control Perimeter." + type = string + default = "5s" +} + +variable "project_budget" { + description = <
budget_amount = optional(number, 1000)
alert_spent_percents = optional(list(number), [1.2])
alert_pubsub_topic = optional(string, null)
alert_spend_basis = optional(string, "FORECASTED_SPEND")
})