main.tf (174 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. */ #to get the pipeline project number data "google_project" "pipeline_project" { project_id = var.project } #to get the cloud run service target project number data "google_project" "cloud_run_project" { for_each = toset(distinct([for project in local.tmp_list_target_sa_run : project.project])) project_id = each.value } locals { tmp_list_target_sa_gke = [for target in var.stage_targets : { project = target.target_spec.project_id exe_sa = target.exe_config_sa_name type = "gke" } if target.target_create && target.target_type == "gke"] tmp_list_target_sa_run = [for target in var.stage_targets : { project = target.target_spec.project_id exe_sa = target.exe_config_sa_name type = "run" } if target.target_create && target.target_type == "run"] #service account created for both gke and run target target_sa = concat(local.tmp_list_target_sa_gke, local.tmp_list_target_sa_run) #assigning actaspermission for execution service account towards cloud run service accounts exe_sa_actas_run_svc_sa = setsubtract(distinct(flatten([for target in var.stage_targets : target.target_create && target.target_type == "run" ? contains(keys(target.target_spec), "run_service_sa") ? [for sa in compact(split(",", target.target_spec.run_service_sa)) : { project = target.target_spec.project_id, exe_sa = target.exe_config_sa_name, run_sa = sa }] : [{}] : [{}]])), [{}]) tmp_list_gke_cluster_sa = [for target in var.stage_targets : target.target_create && target.target_type == "gke" ? [for gke_sa in split(",", target.target_spec.gke_cluster_sa) : gke_sa] : [""]] #assigning storage role to gke service accounts gke_cluster_sa = compact(distinct(flatten(local.tmp_list_gke_cluster_sa))) #assigning actaspermission for trigger service account towards execution service account tri_sa_actas_exe_sa = setsubtract([for target in var.stage_targets : target.target_type == "gke" ? { project = target.target_spec.project_id, exe_sa = target.exe_config_sa_name } : target.target_type == "run" ? { project = target.target_spec.project_id, exe_sa = target.exe_config_sa_name } : {}], [{}]) #service agent binding for cross project service account usage service_agent_binding = setsubtract([for agent_bind in local.target_sa : agent_bind.project != var.project ? agent_bind : {}], [{}]) stage_targets = flatten([for target in var.stage_targets : [for i in target.target_create ? [1] : [] : target]]) } #create cloud deploy pipeline resource "google_clouddeploy_delivery_pipeline" "delivery_pipeline" { depends_on = [module.trigger_service_account, module.execution_service_accounts] location = var.location name = var.pipeline_name project = var.project serial_pipeline { dynamic "stages" { for_each = var.stage_targets content { profiles = stages.value["profiles"] target_id = stages.value["target_name"] dynamic "strategy" { for_each = contains(keys(stages.value.strategy), "standard") ? stages.value.strategy.standard.verify == true ? [1] : [] : [] content { standard { verify = stages.value.strategy.standard.verify } } } } } } } #create cloud deploy targets resource "google_clouddeploy_target" "target" { depends_on = [module.trigger_service_account, module.execution_service_accounts] for_each = { for target in local.stage_targets : target.target_name => target } location = var.location name = each.value.target_name dynamic "gke" { for_each = each.value.target_type == "gke" ? [1] : [] content { cluster = "projects/${each.value.target_spec.project_id}/locations/${each.value.target_spec.location}/clusters/${each.value.target_spec.gke_cluster_name}" internal_ip = lookup(each.value.target_spec, "internal_ip", null) != null ? anytrue([each.value.target_spec.internal_ip]) : anytrue([]) } } dynamic "run" { for_each = each.value.target_type == "run" ? [1] : [] content { location = "projects/${each.value.target_spec.project_id}/locations/${each.value.target_spec.location}" } } require_approval = each.value.require_approval project = var.project execution_configs { usages = ["RENDER", "DEPLOY", "VERIFY"] service_account = each.value.target_type == "gke" || each.value.target_type == "run" ? "${each.value.exe_config_sa_name}@${each.value.target_spec.project_id}.iam.gserviceaccount.com" : null worker_pool = lookup(each.value.execution_config, "worker_pool", null) artifact_storage = lookup(each.value.execution_config, "artifact_storage", null) execution_timeout = lookup(each.value.execution_config, "execution_timeout", null) } } #create trigger service account module "trigger_service_account" { count = var.trigger_sa_create ? 1 : 0 source = "terraform-google-modules/service-accounts/google" version = "~> 4.0" project_id = var.project names = [var.trigger_sa_name] display_name = "TF_managed_${var.trigger_sa_name}" project_roles = [ "${var.project}=>roles/cloudbuild.builds.editor", "${var.project}=>roles/cloudbuild.builds.builder", "${var.project}=>roles/clouddeploy.developer", "${var.project}=>roles/clouddeploy.releaser", "${var.project}=>roles/clouddeploy.jobRunner", "${var.project}=>roles/storage.objectAdmin" ] } #create execution service account module "execution_service_accounts" { for_each = { for i in local.target_sa : "${i.project}=>${i.exe_sa}" => i } source = "terraform-google-modules/service-accounts/google" version = "~> 4.0" project_id = each.value.project names = [each.value.exe_sa] display_name = "TF_managed_${each.value.exe_sa}" project_roles = each.value.type == "gke" ? ["${each.value.project}=>roles/container.developer", "${var.project}=>roles/storage.objectAdmin", "${var.project}=>roles/artifactregistry.reader", "${var.project}=>roles/logging.logWriter", "${each.value.project}=>roles/logging.logWriter"] : each.value.type == "run" ? ["${each.value.project}=>roles/run.developer", "${var.project}=>roles/storage.objectAdmin", "${var.project}=>roles/artifactregistry.reader", "${var.project}=>roles/logging.logWriter", "${each.value.project}=>roles/logging.logWriter"] : [] } #assigning actaspermission for trigger service account towards execution service account resource "google_service_account_iam_member" "tri_sa_actas_exe_sa" { depends_on = [module.execution_service_accounts, module.trigger_service_account] for_each = { for i in local.tri_sa_actas_exe_sa : "${i.project}=>${i.exe_sa}" => i } service_account_id = "projects/${each.value.project}/serviceAccounts/${each.value.exe_sa}@${each.value.project}.iam.gserviceaccount.com" role = "roles/iam.serviceAccountUser" member = "serviceAccount:${var.trigger_sa_name}@${var.project}.iam.gserviceaccount.com" } #assigning actaspermission for execution service account towards cloud run service accounts resource "google_service_account_iam_member" "exe_sa_actas_run_svc_sa" { depends_on = [module.execution_service_accounts, module.trigger_service_account] for_each = { for i in local.exe_sa_actas_run_svc_sa : "${i.exe_sa}=>${i.project}=>${i.run_sa}" => i } service_account_id = "projects/${each.value.project}/serviceAccounts/${each.value.run_sa}" role = "roles/iam.serviceAccountUser" member = "serviceAccount:${each.value.exe_sa}@${each.value.project}.iam.gserviceaccount.com" } #assigning actaspermission for execution service account towards itself. incase exe sa is used to create cloud run service resource "google_service_account_iam_member" "exe_sa_actas_exe_sa" { depends_on = [module.execution_service_accounts] for_each = { for i in local.tmp_list_target_sa_run : "${i.exe_sa}=>${i.project}" => i } service_account_id = "projects/${each.value.project}/serviceAccounts/${each.value.exe_sa}@${each.value.project}.iam.gserviceaccount.com" role = "roles/iam.serviceAccountUser" member = "serviceAccount:${each.value.exe_sa}@${each.value.project}.iam.gserviceaccount.com" } #service agent binding for cross project service account usage. https://cloud.google.com/deploy/docs/cloud-deploy-service-account resource "google_service_account_iam_member" "cloud_build_service_agent_actas_deploy_sa" { depends_on = [module.execution_service_accounts, module.trigger_service_account, data.google_project.pipeline_project] for_each = { for i in local.service_agent_binding : "${i.project}=>${i.exe_sa}" => i } service_account_id = "projects/${each.value.project}/serviceAccounts/${each.value.exe_sa}@${each.value.project}.iam.gserviceaccount.com" role = "roles/iam.serviceAccountTokenCreator" member = "serviceAccount:service-${data.google_project.pipeline_project.number}@gcp-sa-cloudbuild.iam.gserviceaccount.com" } resource "google_service_account_iam_member" "cloud_deploy_service_agent_actas_deploy_sa" { depends_on = [module.execution_service_accounts, module.trigger_service_account, data.google_project.pipeline_project] for_each = { for i in local.service_agent_binding : "${i.project}=>${i.exe_sa}" => i } service_account_id = "projects/${each.value.project}/serviceAccounts/${each.value.exe_sa}@${each.value.project}.iam.gserviceaccount.com" role = "roles/iam.serviceAccountUser" member = "serviceAccount:service-${data.google_project.pipeline_project.number}@gcp-sa-clouddeploy.iam.gserviceaccount.com" } resource "google_project_iam_member" "cloud_run_service_agent_binding_storage_viewer" { for_each = toset(distinct(setsubtract([for i in local.tmp_list_target_sa_run : i.project], [var.project]))) project = var.project role = "roles/storage.objectViewer" member = "serviceAccount:service-${data.google_project.cloud_run_project[each.value].number}@serverless-robot-prod.iam.gserviceaccount.com" } resource "google_project_iam_member" "gke_cluster_sa_to_storage_viewer" { for_each = toset(local.gke_cluster_sa) project = var.project role = "roles/storage.objectViewer" member = "serviceAccount:${each.value}" } resource "google_project_iam_member" "cloud_run_service_agent_binding_artifact_reader" { for_each = toset(distinct(setsubtract([for i in local.tmp_list_target_sa_run : i.project], [var.project]))) project = var.project role = "roles/artifactregistry.reader" member = "serviceAccount:service-${data.google_project.cloud_run_project[each.value].number}@serverless-robot-prod.iam.gserviceaccount.com" } resource "google_project_iam_member" "gke_cluster_sa_to_artifact_reader" { for_each = toset(local.gke_cluster_sa) project = var.project role = "roles/artifactregistry.reader" member = "serviceAccount:${each.value}" }