From e3cffc4a882739c14550d59e7aa84ce7c098c5ae Mon Sep 17 00:00:00 2001 From: Calvin Cheng Date: Wed, 9 Sep 2026 05:50:41 +0000 Subject: [PATCH 1/2] feat: migrate CI/CD repository type from sourcerepo to ssm and update IAM roles to include Secure Source Manager accessors --- fast/stages-aw/0-bootstrap/IAM.md | 4 +- fast/stages-aw/0-bootstrap/README.md | 2 +- fast/stages-aw/0-bootstrap/automation.tf | 6 +- fast/stages-aw/0-bootstrap/cicd.tf | 78 +++++++----- ...flow-sourcerepo.yaml => workflow-ssm.yaml} | 0 fast/stages-aw/0-bootstrap/variables.tf | 8 +- fast/stages-aw/1-resman/cicd-data-platform.tf | 12 +- fast/stages-aw/1-resman/cicd-gcve.tf | 12 +- fast/stages-aw/1-resman/cicd-gke.tf | 12 +- fast/stages-aw/1-resman/cicd-networking.tf | 6 +- .../1-resman/cicd-project-factory.tf | 12 +- fast/stages-aw/1-resman/cicd-security.tf | 6 +- fast/stages-aw/1-resman/cicd-teams.tf | 4 +- fast/stages-aw/1-resman/main.tf | 4 +- fast/stages-aw/1-resman/outputs.tf | 114 +++++++++--------- ...flow-sourcerepo.yaml => workflow-ssm.yaml} | 0 fast/stages-aw/1-resman/variables.tf | 8 +- 17 files changed, 150 insertions(+), 138 deletions(-) rename fast/stages-aw/0-bootstrap/templates/{workflow-sourcerepo.yaml => workflow-ssm.yaml} (100%) rename fast/stages-aw/1-resman/templates/{workflow-sourcerepo.yaml => workflow-ssm.yaml} (100%) diff --git a/fast/stages-aw/0-bootstrap/IAM.md b/fast/stages-aw/0-bootstrap/IAM.md index 7a0ed0677..6995320dc 100644 --- a/fast/stages-aw/0-bootstrap/IAM.md +++ b/fast/stages-aw/0-bootstrap/IAM.md @@ -35,7 +35,7 @@ Legend: + additive, conditional. |prod-bootstrap-0r
serviceAccount|organizations/[organization #0]/roles/storageViewer
[roles/viewer](https://cloud.google.com/iam/docs/understanding-roles#viewer) | |prod-bootstrap-1
serviceAccount|[roles/logging.logWriter](https://cloud.google.com/iam/docs/understanding-roles#logging.logWriter) +| |prod-bootstrap-1r
serviceAccount|[roles/logging.logWriter](https://cloud.google.com/iam/docs/understanding-roles#logging.logWriter) +| -|prod-resman-0
serviceAccount|[roles/cloudbuild.builds.editor](https://cloud.google.com/iam/docs/understanding-roles#cloudbuild.builds.editor)
[roles/iam.serviceAccountAdmin](https://cloud.google.com/iam/docs/understanding-roles#iam.serviceAccountAdmin)
[roles/iam.workloadIdentityPoolAdmin](https://cloud.google.com/iam/docs/understanding-roles#iam.workloadIdentityPoolAdmin)
[roles/source.admin](https://cloud.google.com/iam/docs/understanding-roles#source.admin)
[roles/storage.admin](https://cloud.google.com/iam/docs/understanding-roles#storage.admin)
[roles/resourcemanager.projectIamAdmin](https://cloud.google.com/iam/docs/understanding-roles#resourcemanager.projectIamAdmin)
[roles/serviceusage.serviceUsageConsumer](https://cloud.google.com/iam/docs/understanding-roles#serviceusage.serviceUsageConsumer) +| -|prod-resman-0r
serviceAccount|organizations/[organization #0]/roles/storageViewer
[roles/browser](https://cloud.google.com/iam/docs/understanding-roles#browser)
[roles/cloudbuild.builds.viewer](https://cloud.google.com/iam/docs/understanding-roles#cloudbuild.builds.viewer)
[roles/iam.serviceAccountViewer](https://cloud.google.com/iam/docs/understanding-roles#iam.serviceAccountViewer)
[roles/iam.workloadIdentityPoolViewer](https://cloud.google.com/iam/docs/understanding-roles#iam.workloadIdentityPoolViewer)
[roles/source.reader](https://cloud.google.com/iam/docs/understanding-roles#source.reader)
[roles/viewer](https://cloud.google.com/iam/docs/understanding-roles#viewer)
[roles/serviceusage.serviceUsageViewer](https://cloud.google.com/iam/docs/understanding-roles#serviceusage.serviceUsageViewer) +| +|prod-resman-0
serviceAccount|[roles/cloudbuild.builds.editor](https://cloud.google.com/iam/docs/understanding-roles#cloudbuild.builds.editor)
[roles/iam.serviceAccountAdmin](https://cloud.google.com/iam/docs/understanding-roles#iam.serviceAccountAdmin)
[roles/iam.workloadIdentityPoolAdmin](https://cloud.google.com/iam/docs/understanding-roles#iam.workloadIdentityPoolAdmin)
[roles/securesourcemanager.instanceAccessor](https://cloud.google.com/iam/docs/understanding-roles#securesourcemanager.instanceAccessor)
[roles/storage.admin](https://cloud.google.com/iam/docs/understanding-roles#storage.admin)
[roles/resourcemanager.projectIamAdmin](https://cloud.google.com/iam/docs/understanding-roles#resourcemanager.projectIamAdmin)
[roles/serviceusage.serviceUsageConsumer](https://cloud.google.com/iam/docs/understanding-roles#serviceusage.serviceUsageConsumer) +| +|prod-resman-0r
serviceAccount|organizations/[organization #0]/roles/storageViewer
[roles/browser](https://cloud.google.com/iam/docs/understanding-roles#browser)
[roles/cloudbuild.builds.viewer](https://cloud.google.com/iam/docs/understanding-roles#cloudbuild.builds.viewer)
[roles/iam.serviceAccountViewer](https://cloud.google.com/iam/docs/understanding-roles#iam.serviceAccountViewer)
[roles/iam.workloadIdentityPoolViewer](https://cloud.google.com/iam/docs/understanding-roles#iam.workloadIdentityPoolViewer)
[roles/securesourcemanager.instanceAccessor](https://cloud.google.com/iam/docs/understanding-roles#securesourcemanager.instanceAccessor)
[roles/viewer](https://cloud.google.com/iam/docs/understanding-roles#viewer)
[roles/serviceusage.serviceUsageViewer](https://cloud.google.com/iam/docs/understanding-roles#serviceusage.serviceUsageViewer) +| |prod-resman-1
serviceAccount|[roles/logging.logWriter](https://cloud.google.com/iam/docs/understanding-roles#logging.logWriter) +| |prod-resman-1r
serviceAccount|[roles/logging.logWriter](https://cloud.google.com/iam/docs/understanding-roles#logging.logWriter) +| diff --git a/fast/stages-aw/0-bootstrap/README.md b/fast/stages-aw/0-bootstrap/README.md index babb90a5a..859feeb67 100644 --- a/fast/stages-aw/0-bootstrap/README.md +++ b/fast/stages-aw/0-bootstrap/README.md @@ -325,7 +325,7 @@ cicd_repositories = { } ``` -The `type` attribute can be set to one of the supported repository types: `github`, `gitlab`, or `sourcerepo`. +The `type` attribute can be set to one of the supported repository types: `github`, `gitlab`, or `ssm`. Once the stage is applied the generated output files will contain pre-configured workflow files for each repository, that will use Workload Identity Federation via a dedicated service account for each repository to impersonate the automation service account for the stage. diff --git a/fast/stages-aw/0-bootstrap/automation.tf b/fast/stages-aw/0-bootstrap/automation.tf index 6e9925403..1e99e300a 100644 --- a/fast/stages-aw/0-bootstrap/automation.tf +++ b/fast/stages-aw/0-bootstrap/automation.tf @@ -74,10 +74,8 @@ module "automation-project" { "roles/iam.workloadIdentityPoolViewer" = [ module.automation-tf-resman-r-sa.iam_email ] - "roles/source.admin" = [ - module.automation-tf-resman-sa.iam_email - ] - "roles/source.reader" = [ + "roles/securesourcemanager.instanceAccessor" = [ + module.automation-tf-resman-sa.iam_email, module.automation-tf-resman-r-sa.iam_email ] "roles/storage.admin" = [ diff --git a/fast/stages-aw/0-bootstrap/cicd.tf b/fast/stages-aw/0-bootstrap/cicd.tf index abd857f34..85e1df4bd 100644 --- a/fast/stages-aw/0-bootstrap/cicd.tf +++ b/fast/stages-aw/0-bootstrap/cicd.tf @@ -37,7 +37,7 @@ locals { v != null && ( - try(v.type, null) == "sourcerepo" + try(v.type, null) == "ssm" || contains( keys(local.workload_identity_providers), @@ -65,44 +65,58 @@ locals { } } -# source repository +# secure source manager instance and repository module "automation-tf-cicd-repo" { - source = "../../../modules/source-repository" + source = "../../../modules/secure-source-manager-instance" for_each = { - for k, v in local.cicd_repositories : k => v if v.type == "sourcerepo" + for k, v in local.cicd_repositories : k => v if v.type == "ssm" } - project_id = module.automation-project.project_id - name = each.value.name - iam = { - "roles/source.admin" = [ - each.key == "bootstrap" - ? module.automation-tf-bootstrap-sa.iam_email - : module.automation-tf-resman-sa.iam_email - ] - "roles/source.reader" = concat( - [module.automation-tf-cicd-sa[each.key].iam_email], - each.key == "bootstrap" - ? [module.automation-tf-bootstrap-r-sa.iam_email] - : [module.automation-tf-resman-r-sa.iam_email] - ) - } - triggers = { - "fast-0-${each.key}" = { - filename = ".cloudbuild/workflow.yaml" - included_files = ["**/*tf", ".cloudbuild/workflow.yaml"] - service_account = module.automation-tf-cicd-sa[each.key].id - substitutions = {} - template = { - project_id = null - branch_name = each.value.branch - repo_name = each.value.name - tag_name = null + project_id = module.automation-project.project_id + location = var.regions.primary + instance_id = "${each.key}-repo-instance" + instance_create = true + repositories = { + (each.value.name) = { + description = "FAST Secure Source Manager repository for ${each.key} stage." + initial_config = { + default_branch = coalesce(each.value.branch, "main") + } + iam = { + "roles/securesourcemanager.repoAdmin" = [ + each.key == "bootstrap" + ? module.automation-tf-bootstrap-sa.iam_email + : module.automation-tf-resman-sa.iam_email + ] + "roles/securesourcemanager.repoReader" = concat( + [module.automation-tf-cicd-sa[each.key].iam_email], + each.key == "bootstrap" + ? [module.automation-tf-bootstrap-r-sa.iam_email] + : [module.automation-tf-resman-r-sa.iam_email] + ) } } } } +resource "google_cloudbuild_trigger" "automation-tf-cicd-trigger" { + for_each = { + for k, v in local.cicd_repositories : k => v if v.type == "ssm" + } + project = module.automation-project.project_id + name = "fast-0-${each.key}" + + repository_event_config { + repository = module.automation-tf-cicd-repo[each.key].repositories[each.value.name].id + push { + branch = "^${coalesce(each.value.branch, "main")}$" + } + } + + filename = ".cloudbuild/workflow.yaml" + service_account = module.automation-tf-cicd-sa[each.key].id +} + # SAs used by CI/CD workflows to impersonate automation SAs module "automation-tf-cicd-sa" { @@ -113,7 +127,7 @@ module "automation-tf-cicd-sa" { display_name = "Terraform CI/CD ${each.key} service account." prefix = local.prefix iam = ( - each.value.type == "sourcerepo" + each.value.type == "ssm" # used directly from the cloud build trigger for source repos ? {} # impersonated via workload identity federation for external repos @@ -150,7 +164,7 @@ module "automation-tf-cicd-r-sa" { display_name = "Terraform CI/CD ${each.key} service account (read-only)." prefix = local.prefix iam = ( - each.value.type == "sourcerepo" + each.value.type == "ssm" # build trigger for read-only SA is optionally defined by users ? {} # impersonated via workload identity federation for external repos diff --git a/fast/stages-aw/0-bootstrap/templates/workflow-sourcerepo.yaml b/fast/stages-aw/0-bootstrap/templates/workflow-ssm.yaml similarity index 100% rename from fast/stages-aw/0-bootstrap/templates/workflow-sourcerepo.yaml rename to fast/stages-aw/0-bootstrap/templates/workflow-ssm.yaml diff --git a/fast/stages-aw/0-bootstrap/variables.tf b/fast/stages-aw/0-bootstrap/variables.tf index e7970f9f1..ce2444142 100644 --- a/fast/stages-aw/0-bootstrap/variables.tf +++ b/fast/stages-aw/0-bootstrap/variables.tf @@ -100,19 +100,19 @@ variable "cicd_repositories" { v == null || ( try(v.identity_provider, null) != null || - try(v.type, null) == "sourcerepo" + try(v.type, null) == "ssm" ) ]) - error_message = "Non-null repositories need a non-null provider unless type is 'sourcerepo'." + error_message = "Non-null repositories need a non-null provider unless type is 'ssm'." } validation { condition = alltrue([ for k, v in coalesce(var.cicd_repositories, {}) : v == null || ( - contains(["github", "gitlab", "sourcerepo"], coalesce(try(v.type, null), "null")) + contains(["github", "gitlab", "ssm"], coalesce(try(v.type, null), "null")) ) ]) - error_message = "Invalid repository type, supported types: 'github' 'gitlab' or 'sourcerepo'." + error_message = "Invalid repository type, supported types: 'github' 'gitlab' or 'ssm'." } } diff --git a/fast/stages-aw/1-resman/cicd-data-platform.tf b/fast/stages-aw/1-resman/cicd-data-platform.tf index a544f5c36..3024bd1bb 100644 --- a/fast/stages-aw/1-resman/cicd-data-platform.tf +++ b/fast/stages-aw/1-resman/cicd-data-platform.tf @@ -21,7 +21,7 @@ module "branch-dp-dev-cicd-repo" { source = "../../../modules/source-repository" for_each = ( - try(local.cicd_repositories.data_platform_dev.type, null) == "sourcerepo" + try(local.cicd_repositories.data_platform_dev.type, null) == "ssm" ? { 0 = local.cicd_repositories.data_platform_dev } : {} ) @@ -57,7 +57,7 @@ module "branch-dp-dev-cicd-repo" { module "branch-dp-prod-cicd-repo" { source = "../../../modules/source-repository" for_each = ( - try(local.cicd_repositories.data_platform_prod.type, null) == "sourcerepo" + try(local.cicd_repositories.data_platform_prod.type, null) == "ssm" ? { 0 = local.cicd_repositories.data_platform_prod } : {} ) @@ -100,7 +100,7 @@ module "branch-dp-dev-sa-cicd" { display_name = "Terraform CI/CD data platform development service account." prefix = var.prefix iam = ( - each.value.type == "sourcerepo" + each.value.type == "ssm" # used directly from the cloud build trigger for source repos ? { "roles/iam.serviceAccountUser" = local.automation_resman_sa_iam @@ -143,7 +143,7 @@ module "branch-dp-prod-sa-cicd" { display_name = "Terraform CI/CD data platform production service account." prefix = var.prefix iam = ( - each.value.type == "sourcerepo" + each.value.type == "ssm" # used directly from the cloud build trigger for source repos ? { "roles/iam.serviceAccountUser" = local.automation_resman_sa_iam @@ -188,7 +188,7 @@ module "branch-dp-dev-r-sa-cicd" { display_name = "Terraform CI/CD data platform development service account (read-only)." prefix = var.prefix iam = ( - each.value.type == "sourcerepo" + each.value.type == "ssm" # build trigger for read-only SA is optionally defined by users ? {} # impersonated via workload identity federation for external repos @@ -222,7 +222,7 @@ module "branch-dp-prod-r-sa-cicd" { display_name = "Terraform CI/CD data platform production service account (read-only)." prefix = var.prefix iam = ( - each.value.type == "sourcerepo" + each.value.type == "ssm" # build trigger for read-only SA is optionally defined by users ? {} # impersonated via workload identity federation for external repos diff --git a/fast/stages-aw/1-resman/cicd-gcve.tf b/fast/stages-aw/1-resman/cicd-gcve.tf index 119ae8bba..4acdff0ac 100644 --- a/fast/stages-aw/1-resman/cicd-gcve.tf +++ b/fast/stages-aw/1-resman/cicd-gcve.tf @@ -21,7 +21,7 @@ module "branch-gcve-dev-cicd-repo" { source = "../../../modules/source-repository" for_each = ( - try(local.cicd_repositories.gcve_dev.type, null) == "sourcerepo" + try(local.cicd_repositories.gcve_dev.type, null) == "ssm" ? { 0 = local.cicd_repositories.gcve_dev } : {} ) @@ -57,7 +57,7 @@ module "branch-gcve-dev-cicd-repo" { module "branch-gcve-prod-cicd-repo" { source = "../../../modules/source-repository" for_each = ( - try(local.cicd_repositories.gcve_prod.type, null) == "sourcerepo" + try(local.cicd_repositories.gcve_prod.type, null) == "ssm" ? { 0 = local.cicd_repositories.gcve_prod } : {} ) @@ -100,7 +100,7 @@ module "branch-gcve-dev-sa-cicd" { display_name = "Terraform CI/CD GCVE development service account." prefix = var.prefix iam = ( - each.value.type == "sourcerepo" + each.value.type == "ssm" # used directly from the cloud build trigger for source repos ? { "roles/iam.serviceAccountUser" = local.automation_resman_sa_iam @@ -143,7 +143,7 @@ module "branch-gcve-prod-sa-cicd" { display_name = "Terraform CI/CD GCVE production service account." prefix = var.prefix iam = ( - each.value.type == "sourcerepo" + each.value.type == "ssm" # used directly from the cloud build trigger for source repos ? { "roles/iam.serviceAccountUser" = local.automation_resman_sa_iam @@ -188,7 +188,7 @@ module "branch-gcve-dev-r-sa-cicd" { display_name = "Terraform CI/CD GCVE development service account (read-only)." prefix = var.prefix iam = ( - each.value.type == "sourcerepo" + each.value.type == "ssm" # build trigger for read-only SA is optionally defined by users ? {} # impersonated via workload identity federation for external repos @@ -222,7 +222,7 @@ module "branch-gcve-prod-r-sa-cicd" { display_name = "Terraform CI/CD GCVE production service account (read-only)." prefix = var.prefix iam = ( - each.value.type == "sourcerepo" + each.value.type == "ssm" # build trigger for read-only SA is optionally defined by users ? {} # impersonated via workload identity federation for external repos diff --git a/fast/stages-aw/1-resman/cicd-gke.tf b/fast/stages-aw/1-resman/cicd-gke.tf index 4125104bb..5e942b833 100644 --- a/fast/stages-aw/1-resman/cicd-gke.tf +++ b/fast/stages-aw/1-resman/cicd-gke.tf @@ -21,7 +21,7 @@ module "branch-gke-dev-cicd-repo" { source = "../../../modules/source-repository" for_each = ( - try(local.cicd_repositories.gke_dev.type, null) == "sourcerepo" + try(local.cicd_repositories.gke_dev.type, null) == "ssm" ? { 0 = local.cicd_repositories.gke_dev } : {} ) @@ -57,7 +57,7 @@ module "branch-gke-dev-cicd-repo" { module "branch-gke-prod-cicd-repo" { source = "../../../modules/source-repository" for_each = ( - try(local.cicd_repositories.gke_prod.type, null) == "sourcerepo" + try(local.cicd_repositories.gke_prod.type, null) == "ssm" ? { 0 = local.cicd_repositories.gke_prod } : {} ) @@ -100,7 +100,7 @@ module "branch-gke-dev-sa-cicd" { display_name = "Terraform CI/CD GKE development service account." prefix = var.prefix iam = ( - each.value.type == "sourcerepo" + each.value.type == "ssm" # used directly from the cloud build trigger for source repos ? { "roles/iam.serviceAccountUser" = local.automation_resman_sa_iam @@ -143,7 +143,7 @@ module "branch-gke-prod-sa-cicd" { display_name = "Terraform CI/CD GKE production service account." prefix = var.prefix iam = ( - each.value.type == "sourcerepo" + each.value.type == "ssm" # used directly from the cloud build trigger for source repos ? { "roles/iam.serviceAccountUser" = local.automation_resman_sa_iam @@ -188,7 +188,7 @@ module "branch-gke-dev-r-sa-cicd" { display_name = "Terraform CI/CD gke multitenant development service account (read-only)." prefix = var.prefix iam = ( - each.value.type == "sourcerepo" + each.value.type == "ssm" # build trigger for read-only SA is optionally defined by users ? {} # impersonated via workload identity federation for external repos @@ -222,7 +222,7 @@ module "branch-gke-prod-r-sa-cicd" { display_name = "Terraform CI/CD gke multitenant production service account (read-only)." prefix = var.prefix iam = ( - each.value.type == "sourcerepo" + each.value.type == "ssm" # build trigger for read-only SA is optionally defined by users ? {} # impersonated via workload identity federation for external repos diff --git a/fast/stages-aw/1-resman/cicd-networking.tf b/fast/stages-aw/1-resman/cicd-networking.tf index 2215e9baa..6cada18d2 100644 --- a/fast/stages-aw/1-resman/cicd-networking.tf +++ b/fast/stages-aw/1-resman/cicd-networking.tf @@ -21,7 +21,7 @@ module "branch-network-cicd-repo" { source = "../../../modules/source-repository" for_each = ( - try(local.cicd_repositories.networking.type, null) == "sourcerepo" + try(local.cicd_repositories.networking.type, null) == "ssm" ? { 0 = local.cicd_repositories.networking } : {} ) @@ -62,7 +62,7 @@ module "branch-network-sa-cicd" { display_name = "Terraform CI/CD stage 2 networking service account." prefix = var.prefix iam = ( - each.value.type == "sourcerepo" + each.value.type == "ssm" # used directly from the cloud build trigger for source repos ? { "roles/iam.serviceAccountUser" = local.automation_resman_sa_iam @@ -107,7 +107,7 @@ module "branch-network-r-sa-cicd" { display_name = "Terraform CI/CD stage 2 networking service account (read-only)." prefix = var.prefix iam = ( - each.value.type == "sourcerepo" + each.value.type == "ssm" # build trigger for read-only SA is optionally defined by users ? {} # impersonated via workload identity federation for external repos diff --git a/fast/stages-aw/1-resman/cicd-project-factory.tf b/fast/stages-aw/1-resman/cicd-project-factory.tf index 009199c2e..ff7e78ef8 100644 --- a/fast/stages-aw/1-resman/cicd-project-factory.tf +++ b/fast/stages-aw/1-resman/cicd-project-factory.tf @@ -21,7 +21,7 @@ module "branch-pf-dev-cicd-repo" { source = "../../../modules/source-repository" for_each = ( - try(local.cicd_repositories.project_factory_dev.type, null) == "sourcerepo" + try(local.cicd_repositories.project_factory_dev.type, null) == "ssm" ? { 0 = local.cicd_repositories.project_factory_dev } : {} ) @@ -53,7 +53,7 @@ module "branch-pf-dev-cicd-repo" { module "branch-pf-prod-cicd-repo" { source = "../../../modules/source-repository" for_each = ( - try(local.cicd_repositories.project_factory_prod.type, null) == "sourcerepo" + try(local.cicd_repositories.project_factory_prod.type, null) == "ssm" ? { 0 = local.cicd_repositories.project_factory_prod } : {} ) @@ -96,7 +96,7 @@ module "branch-pf-dev-sa-cicd" { display_name = "Terraform CI/CD project factory development service account." prefix = var.prefix iam = ( - each.value.type == "sourcerepo" + each.value.type == "ssm" # used directly from the cloud build trigger for source repos ? { "roles/iam.serviceAccountUser" = local.automation_resman_sa_iam @@ -139,7 +139,7 @@ module "branch-pf-prod-sa-cicd" { display_name = "Terraform CI/CD project factory production service account." prefix = var.prefix iam = ( - each.value.type == "sourcerepo" + each.value.type == "ssm" # used directly from the cloud build trigger for source repos ? { "roles/iam.serviceAccountUser" = local.automation_resman_sa_iam @@ -184,7 +184,7 @@ module "branch-pf-dev-r-sa-cicd" { display_name = "Terraform CI/CD project factory development service account (read-only)." prefix = var.prefix iam = ( - each.value.type == "sourcerepo" + each.value.type == "ssm" # build trigger for read-only SA is optionally defined by users ? {} # impersonated via workload identity federation for external repos @@ -218,7 +218,7 @@ module "branch-pf-prod-r-sa-cicd" { display_name = "Terraform CI/CD project factory production service account (read-only)." prefix = var.prefix iam = ( - each.value.type == "sourcerepo" + each.value.type == "ssm" # build trigger for read-only SA is optionally defined by users ? {} # impersonated via workload identity federation for external repos diff --git a/fast/stages-aw/1-resman/cicd-security.tf b/fast/stages-aw/1-resman/cicd-security.tf index d0dab9236..9b98a4e54 100644 --- a/fast/stages-aw/1-resman/cicd-security.tf +++ b/fast/stages-aw/1-resman/cicd-security.tf @@ -21,7 +21,7 @@ module "branch-security-cicd-repo" { source = "../../../modules/source-repository" for_each = ( - try(local.cicd_repositories.security.type, null) == "sourcerepo" + try(local.cicd_repositories.security.type, null) == "ssm" ? { 0 = local.cicd_repositories.security } : {} ) @@ -62,7 +62,7 @@ module "branch-security-sa-cicd" { display_name = "Terraform CI/CD stage 2 security service account." prefix = var.prefix iam = ( - each.value.type == "sourcerepo" + each.value.type == "ssm" # used directly from the cloud build trigger for source repos ? { "roles/iam.serviceAccountUser" = local.automation_resman_sa_iam @@ -107,7 +107,7 @@ module "branch-security-r-sa-cicd" { display_name = "Terraform CI/CD stage 2 security service account (read-only)." prefix = var.prefix iam = ( - each.value.type == "sourcerepo" + each.value.type == "ssm" # build trigger for read-only SA is optionally defined by users ? {} # impersonated via workload identity federation for external repos diff --git a/fast/stages-aw/1-resman/cicd-teams.tf b/fast/stages-aw/1-resman/cicd-teams.tf index 67e04c109..c1d06739f 100644 --- a/fast/stages-aw/1-resman/cicd-teams.tf +++ b/fast/stages-aw/1-resman/cicd-teams.tf @@ -22,7 +22,7 @@ module "branch-teams-team-cicd-repo" { source = "../../../modules/source-repository" for_each = { for k, v in coalesce(local.team_cicd_repositories, {}) : k => v - if v.cicd.type == "sourcerepo" + if v.cicd.type == "ssm" } project_id = var.automation.project_id name = each.value.cicd.name @@ -61,7 +61,7 @@ module "branch-teams-team-sa-cicd" { display_name = "Terraform CI/CD team ${each.key} service account." prefix = var.prefix iam = ( - each.value.cicd.type == "sourcerepo" + each.value.cicd.type == "ssm" # used directly from the cloud build trigger for source repos ? { "roles/iam.serviceAccountUser" = local.automation_resman_sa_iam diff --git a/fast/stages-aw/1-resman/main.tf b/fast/stages-aw/1-resman/main.tf index 293f75c77..211e22cf5 100644 --- a/fast/stages-aw/1-resman/main.tf +++ b/fast/stages-aw/1-resman/main.tf @@ -50,7 +50,7 @@ locals { if( v != null && ( - try(v.type, null) == "sourcerepo" + try(v.type, null) == "ssm" || contains( keys(local.identity_providers), @@ -65,7 +65,7 @@ locals { if( v != null && ( - try(v.cicd.type, null) == "sourcerepo" + try(v.cicd.type, null) == "ssm" || contains( keys(local.identity_providers), diff --git a/fast/stages-aw/1-resman/outputs.tf b/fast/stages-aw/1-resman/outputs.tf index 4a6f8679a..b1a29ce35 100644 --- a/fast/stages-aw/1-resman/outputs.tf +++ b/fast/stages-aw/1-resman/outputs.tf @@ -200,113 +200,113 @@ locals { }, !var.fast_features.data_platform ? {} : { "3-data-platform-dev" = { - bucket = module.branch-dp-dev-gcs[0].name - name = "dp-dev" - sa = module.branch-dp-dev-sa[0].email + bucket = module.branch-dp-dev-gcs[0].name + name = "dp-dev" + sa = module.branch-dp-dev-sa[0].email } "3-data-platform-dev-r" = { - bucket = module.branch-dp-dev-gcs[0].name - name = "dp-dev" - sa = module.branch-dp-dev-r-sa[0].email + bucket = module.branch-dp-dev-gcs[0].name + name = "dp-dev" + sa = module.branch-dp-dev-r-sa[0].email } "3-data-platform-prod" = { - bucket = module.branch-dp-prod-gcs[0].name - name = "dp-prod" - sa = module.branch-dp-prod-sa[0].email + bucket = module.branch-dp-prod-gcs[0].name + name = "dp-prod" + sa = module.branch-dp-prod-sa[0].email } "3-data-platform-prod-r" = { - bucket = module.branch-dp-prod-gcs[0].name - name = "dp-prod" - sa = module.branch-dp-prod-r-sa[0].email + bucket = module.branch-dp-prod-gcs[0].name + name = "dp-prod" + sa = module.branch-dp-prod-r-sa[0].email } }, !var.fast_features.gke ? {} : { "3-gke-dev" = { - bucket = module.branch-gke-dev-gcs[0].name - name = "gke-dev" - sa = module.branch-gke-dev-sa[0].email + bucket = module.branch-gke-dev-gcs[0].name + name = "gke-dev" + sa = module.branch-gke-dev-sa[0].email } "3-gke-dev-r" = { - bucket = module.branch-gke-dev-gcs[0].name - name = "gke-dev" - sa = module.branch-gke-dev-r-sa[0].email + bucket = module.branch-gke-dev-gcs[0].name + name = "gke-dev" + sa = module.branch-gke-dev-r-sa[0].email } "3-gke-prod" = { - bucket = module.branch-gke-prod-gcs[0].name - name = "gke-prod" - sa = module.branch-gke-prod-sa[0].email + bucket = module.branch-gke-prod-gcs[0].name + name = "gke-prod" + sa = module.branch-gke-prod-sa[0].email } "3-gke-prod-r" = { - bucket = module.branch-gke-prod-gcs[0].name - name = "gke-prod" - sa = module.branch-gke-prod-r-sa[0].email + bucket = module.branch-gke-prod-gcs[0].name + name = "gke-prod" + sa = module.branch-gke-prod-r-sa[0].email } }, !var.fast_features.gcve ? {} : { "3-gcve-dev" = { - bucket = module.branch-gcve-dev-gcs[0].name - name = "gcve-dev" - sa = module.branch-gcve-dev-sa[0].email + bucket = module.branch-gcve-dev-gcs[0].name + name = "gcve-dev" + sa = module.branch-gcve-dev-sa[0].email } "3-gcve-dev-r" = { - bucket = module.branch-gcve-dev-gcs[0].name - name = "gcve-dev" - sa = module.branch-gcve-dev-r-sa[0].email + bucket = module.branch-gcve-dev-gcs[0].name + name = "gcve-dev" + sa = module.branch-gcve-dev-r-sa[0].email } "3-gcve-prod" = { - bucket = module.branch-gcve-prod-gcs[0].name - name = "gcve-prod" - sa = module.branch-gcve-prod-sa[0].email + bucket = module.branch-gcve-prod-gcs[0].name + name = "gcve-prod" + sa = module.branch-gcve-prod-sa[0].email } "3-gcve-prod-r" = { - bucket = module.branch-gcve-prod-gcs[0].name - name = "gcve-prod" - sa = module.branch-gcve-prod-r-sa[0].email + bucket = module.branch-gcve-prod-gcs[0].name + name = "gcve-prod" + sa = module.branch-gcve-prod-r-sa[0].email } }, !var.fast_features.project_factory ? {} : { "3-project-factory-dev" = { - bucket = module.branch-pf-dev-gcs[0].name - name = "team-dev" - sa = module.branch-pf-dev-sa[0].email + bucket = module.branch-pf-dev-gcs[0].name + name = "team-dev" + sa = module.branch-pf-dev-sa[0].email } "3-project-factory-dev-r" = { - bucket = module.branch-pf-dev-gcs[0].name - name = "team-dev" - sa = module.branch-pf-dev-r-sa[0].email + bucket = module.branch-pf-dev-gcs[0].name + name = "team-dev" + sa = module.branch-pf-dev-r-sa[0].email } "3-project-factory-prod" = { - bucket = module.branch-pf-prod-gcs[0].name - name = "team-prod" - sa = module.branch-pf-prod-sa[0].email + bucket = module.branch-pf-prod-gcs[0].name + name = "team-prod" + sa = module.branch-pf-prod-sa[0].email } "3-project-factory-prod-r" = { - bucket = module.branch-pf-prod-gcs[0].name - name = "team-prod" - sa = module.branch-pf-prod-r-sa[0].email + bucket = module.branch-pf-prod-gcs[0].name + name = "team-prod" + sa = module.branch-pf-prod-r-sa[0].email } }, !var.fast_features.sandbox ? {} : { "9-sandbox" = { - bucket = module.branch-sandbox-gcs[0].name - name = "sandbox" - sa = module.branch-sandbox-sa[0].email + bucket = module.branch-sandbox-gcs[0].name + name = "sandbox" + sa = module.branch-sandbox-sa[0].email } }, !var.fast_features.teams ? {} : merge( { "3-teams" = { - bucket = module.branch-teams-gcs[0].name - name = "teams" - sa = module.branch-teams-sa[0].email + bucket = module.branch-teams-gcs[0].name + name = "teams" + sa = module.branch-teams-sa[0].email } }, { for k, v in module.branch-teams-team-sa : "3-teams-${k}" => { - bucket = module.branch-teams-team-gcs[k].name - name = "teams" - sa = v.email + bucket = module.branch-teams-team-gcs[k].name + name = "teams" + sa = v.email } } ) diff --git a/fast/stages-aw/1-resman/templates/workflow-sourcerepo.yaml b/fast/stages-aw/1-resman/templates/workflow-ssm.yaml similarity index 100% rename from fast/stages-aw/1-resman/templates/workflow-sourcerepo.yaml rename to fast/stages-aw/1-resman/templates/workflow-ssm.yaml diff --git a/fast/stages-aw/1-resman/variables.tf b/fast/stages-aw/1-resman/variables.tf index c9e38b22b..963712541 100644 --- a/fast/stages-aw/1-resman/variables.tf +++ b/fast/stages-aw/1-resman/variables.tf @@ -143,19 +143,19 @@ variable "cicd_repositories" { v == null || ( try(v.identity_provider, null) != null || - try(v.type, null) == "sourcerepo" + try(v.type, null) == "ssm" ) ]) - error_message = "Non-null repositories need a non-null provider unless type is 'sourcerepo'." + error_message = "Non-null repositories need a non-null provider unless type is 'ssm'." } validation { condition = alltrue([ for k, v in coalesce(var.cicd_repositories, {}) : v == null || ( - contains(["github", "gitlab", "sourcerepo"], coalesce(try(v.type, null), "null")) + contains(["github", "gitlab", "ssm"], coalesce(try(v.type, null), "null")) ) ]) - error_message = "Invalid repository type, supported types: 'github' 'gitlab' or 'sourcerepo'." + error_message = "Invalid repository type, supported types: 'github' 'gitlab' or 'ssm'." } } From dfc705da2c960283fef7a6876263bac27329b6e5 Mon Sep 17 00:00:00 2001 From: Calvin Cheng Date: Wed, 9 Sep 2026 15:04:03 +0000 Subject: [PATCH 2/2] feat: add configuration examples for CI/CD repositories and enable the Secure Source Manager API --- fast/stages-aw/0-bootstrap/automation.tf | 1 + .../0-bootstrap/terraform.tfvars.sample | 30 +++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/fast/stages-aw/0-bootstrap/automation.tf b/fast/stages-aw/0-bootstrap/automation.tf index 1e99e300a..76b04193f 100644 --- a/fast/stages-aw/0-bootstrap/automation.tf +++ b/fast/stages-aw/0-bootstrap/automation.tf @@ -143,6 +143,7 @@ module "automation-project" { "iamcredentials.googleapis.com", "orgpolicy.googleapis.com", "pubsub.googleapis.com", + "securesourcemanager.googleapis.com", "servicenetworking.googleapis.com", "serviceusage.googleapis.com", "stackdriver.googleapis.com", diff --git a/fast/stages-aw/0-bootstrap/terraform.tfvars.sample b/fast/stages-aw/0-bootstrap/terraform.tfvars.sample index 45a361d8a..d375e3531 100644 --- a/fast/stages-aw/0-bootstrap/terraform.tfvars.sample +++ b/fast/stages-aw/0-bootstrap/terraform.tfvars.sample @@ -75,3 +75,33 @@ assured_workloads = { bootstrap_project = "" alert_email = "" + +# CI/CD repository configuration example +# Option A: Secure Source Manager (SSM) +# cicd_repositories = { +# bootstrap = { +# name = "fast-bootstrap" +# type = "ssm" +# branch = "main" +# } +# resman = { +# name = "fast-resman" +# type = "ssm" +# branch = "main" +# } +# } +# Option B: GitHub/GitLab (requires federated_identity_providers) +# cicd_repositories = { +# bootstrap = { +# identity_provider = "github" +# name = "my-org/my-bootstrap-repo" +# type = "github" +# branch = "main" +# } +# resman = { +# identity_provider = "github" +# name = "my-org/my-resman-repo" +# type = "github" +# branch = "main" +# } +# }