From bfefaf627e6ac699851ce6255c6f16c8923e3ca0 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Wed, 3 Aug 2022 14:34:09 +0000 Subject: [PATCH] refactor teams, fix #750 (#766) --- fast/stages/01-resman/README.md | 18 ++--- fast/stages/01-resman/billing.tf | 10 ++- fast/stages/01-resman/branch-networking.tf | 16 ++--- fast/stages/01-resman/branch-teams.tf | 78 +++++++++++++++------- fast/stages/01-resman/main.tf | 7 +- fast/stages/01-resman/organization.tf | 32 +++------ fast/stages/01-resman/outputs.tf | 21 +++++- 7 files changed, 109 insertions(+), 73 deletions(-) diff --git a/fast/stages/01-resman/README.md b/fast/stages/01-resman/README.md index 9f72f77e0f..03c9398193 100644 --- a/fast/stages/01-resman/README.md +++ b/fast/stages/01-resman/README.md @@ -195,14 +195,14 @@ Due to its simplicity, this stage lends itself easily to customizations: adding | name | description | sensitive | consumers | |---|---|:---:|---| -| [cicd_repositories](outputs.tf#L154) | WIF configuration for CI/CD repositories. | | | -| [dataplatform](outputs.tf#L168) | Data for the Data Platform stage. | | | -| [networking](outputs.tf#L184) | Data for the networking stage. | | | -| [project_factories](outputs.tf#L193) | Data for the project factories stage. | | | -| [providers](outputs.tf#L209) | Terraform provider files for this stage and dependent stages. | ✓ | 02-networking · 02-security · 03-dataplatform · xx-sandbox · xx-teams | -| [sandbox](outputs.tf#L216) | Data for the sandbox stage. | | xx-sandbox | -| [security](outputs.tf#L230) | Data for the networking stage. | | 02-security | -| [teams](outputs.tf#L240) | Data for the teams stage. | | | -| [tfvars](outputs.tf#L253) | Terraform variable files for the following stages. | ✓ | | +| [cicd_repositories](outputs.tf#L171) | WIF configuration for CI/CD repositories. | | | +| [dataplatform](outputs.tf#L185) | Data for the Data Platform stage. | | | +| [networking](outputs.tf#L201) | Data for the networking stage. | | | +| [project_factories](outputs.tf#L210) | Data for the project factories stage. | | | +| [providers](outputs.tf#L226) | Terraform provider files for this stage and dependent stages. | ✓ | 02-networking · 02-security · 03-dataplatform · xx-sandbox · xx-teams | +| [sandbox](outputs.tf#L233) | Data for the sandbox stage. | | xx-sandbox | +| [security](outputs.tf#L247) | Data for the networking stage. | | 02-security | +| [teams](outputs.tf#L257) | Data for the teams stage. | | | +| [tfvars](outputs.tf#L270) | Terraform variable files for the following stages. | ✓ | | diff --git a/fast/stages/01-resman/billing.tf b/fast/stages/01-resman/billing.tf index f69b9ad9c0..2a5670b802 100644 --- a/fast/stages/01-resman/billing.tf +++ b/fast/stages/01-resman/billing.tf @@ -23,12 +23,10 @@ locals { module.branch-network-sa.iam_email, module.branch-security-sa.iam_email, ], - local.branch_dataplatform_sa_iam_emails, - # enable if individual teams can create their own projects - # [ - # for k, v in module.branch-teams-team-sa : v.iam_email - # ], - local.branch_teams_pf_sa_iam_emails, + local.branch_optional_sa_lists.dp-dev, + local.branch_optional_sa_lists.dp-prod, + local.branch_optional_sa_lists.pf-dev, + local.branch_optional_sa_lists.pf-prod, ) } diff --git a/fast/stages/01-resman/branch-networking.tf b/fast/stages/01-resman/branch-networking.tf index 684079fad9..00b3a5d747 100644 --- a/fast/stages/01-resman/branch-networking.tf +++ b/fast/stages/01-resman/branch-networking.tf @@ -50,10 +50,10 @@ module "branch-network-prod-folder" { parent = module.branch-network-folder.id name = "Production" iam = { - "roles/compute.xpnAdmin" = compact([ - try(module.branch-dp-prod-sa.0.iam_email, ""), - try(module.branch-pf-prod-sa.0.iam_email, ""), - ]) + (local.custom_roles.service_project_network_admin) = concat( + local.branch_optional_sa_lists.dp-prod, + local.branch_optional_sa_lists.pf-prod, + ) } tag_bindings = { environment = try( @@ -67,10 +67,10 @@ module "branch-network-dev-folder" { parent = module.branch-network-folder.id name = "Development" iam = { - (local.custom_roles.service_project_network_admin) = compact([ - try(module.branch-dp-dev-sa.0.iam_email, ""), - try(module.branch-pf-dev-sa.0.iam_email, ""), - ]) + (local.custom_roles.service_project_network_admin) = concat( + local.branch_optional_sa_lists.dp-dev, + local.branch_optional_sa_lists.pf-dev, + ) } tag_bindings = { environment = try( diff --git a/fast/stages/01-resman/branch-teams.tf b/fast/stages/01-resman/branch-teams.tf index 3c9a2d6968..168e81caf2 100644 --- a/fast/stages/01-resman/branch-teams.tf +++ b/fast/stages/01-resman/branch-teams.tf @@ -21,11 +21,22 @@ moved { to = module.branch-teams-folder.0 } +# TODO(ludo): add support for CI/CD + +############### top-level Teams branch and automation resources ############### + module "branch-teams-folder" { source = "../../../modules/folder" count = var.fast_features.teams ? 1 : 0 parent = "organizations/${var.organization.id}" name = "Teams" + iam = { + "roles/logging.admin" = [module.branch-teams-sa.0.iam_email] + "roles/owner" = [module.branch-teams-sa.0.iam_email] + "roles/resourcemanager.folderAdmin" = [module.branch-teams-sa.0.iam_email] + "roles/resourcemanager.projectCreator" = [module.branch-teams-sa.0.iam_email] + "roles/compute.xpnAdmin" = [module.branch-teams-sa.0.iam_email] + } tag_bindings = { context = try( module.organization.tag_values["${var.tag_names.context}/teams"].id, null @@ -33,27 +44,44 @@ module "branch-teams-folder" { } } -moved { - from = module.branch-teams-prod-sa - to = module.branch-teams-prod-sa.0 -} - -module "branch-teams-prod-sa" { +module "branch-teams-sa" { source = "../../../modules/iam-service-account" count = var.fast_features.teams ? 1 : 0 project_id = var.automation.project_id name = "prod-resman-teams-0" - description = "Terraform resman production service account." + description = "Terraform resman teams service account." prefix = var.prefix + iam_storage_roles = { + (var.automation.outputs_bucket) = ["roles/storage.admin"] + } +} + +module "branch-teams-gcs" { + source = "../../../modules/gcs" + count = var.fast_features.teams ? 1 : 0 + project_id = var.automation.project_id + name = "prod-resman-teams-0" + prefix = var.prefix + versioning = true + iam = { + "roles/storage.objectAdmin" = [module.branch-teams-sa.0.iam_email] + } } -# Team-level folders, service accounts and buckets for each individual team +################## per-team folders and automation resources ################## module "branch-teams-team-folder" { - source = "../../../modules/folder" - for_each = var.fast_features.teams ? coalesce(var.team_folders, {}) : {} - parent = module.branch-teams-folder.0.id - name = each.value.descriptive_name + source = "../../../modules/folder" + for_each = var.fast_features.teams ? coalesce(var.team_folders, {}) : {} + parent = module.branch-teams-folder.0.id + name = each.value.descriptive_name + iam = { + "roles/logging.admin" = [module.branch-teams-team-sa[each.key].iam_email] + "roles/owner" = [module.branch-teams-team-sa[each.key].iam_email] + "roles/resourcemanager.folderAdmin" = [module.branch-teams-team-sa[each.key].iam_email] + "roles/resourcemanager.projectCreator" = [module.branch-teams-team-sa[each.key].iam_email] + "roles/compute.xpnAdmin" = [module.branch-teams-team-sa[each.key].iam_email] + } group_iam = each.value.group_iam == null ? {} : each.value.group_iam } @@ -85,7 +113,7 @@ module "branch-teams-team-gcs" { } } -# project factory per-team environment folders +# per-team environment folders where project factory SAs can create projects module "branch-teams-team-dev-folder" { source = "../../../modules/folder" @@ -96,12 +124,14 @@ module "branch-teams-team-dev-folder" { # environment-wide human permissions on the whole teams environment group_iam = {} iam = { - (local.custom_roles.service_project_network_admin) = [module.branch-pf-dev-sa.0.iam_email] + (local.custom_roles.service_project_network_admin) = ( + local.branch_optional_sa_lists.pf-dev + ) # remove owner here and at project level if SA does not manage project resources - "roles/owner" = [module.branch-pf-dev-sa.0.iam_email] - "roles/logging.admin" = [module.branch-pf-dev-sa.0.iam_email] - "roles/resourcemanager.folderAdmin" = [module.branch-pf-dev-sa.0.iam_email] - "roles/resourcemanager.projectCreator" = [module.branch-pf-dev-sa.0.iam_email] + "roles/owner" = local.branch_optional_sa_lists.pf-dev + "roles/logging.admin" = local.branch_optional_sa_lists.pf-dev + "roles/resourcemanager.folderAdmin" = local.branch_optional_sa_lists.pf-dev + "roles/resourcemanager.projectCreator" = local.branch_optional_sa_lists.pf-dev } tag_bindings = { environment = try( @@ -119,12 +149,14 @@ module "branch-teams-team-prod-folder" { # environment-wide human permissions on the whole teams environment group_iam = {} iam = { - (local.custom_roles.service_project_network_admin) = [module.branch-pf-prod-sa.0.iam_email] + (local.custom_roles.service_project_network_admin) = ( + local.branch_optional_sa_lists.pf-prod + ) # remove owner here and at project level if SA does not manage project resources - "roles/owner" = [module.branch-pf-prod-sa.0.iam_email] - "roles/logging.admin" = [module.branch-pf-prod-sa.0.iam_email] - "roles/resourcemanager.folderAdmin" = [module.branch-pf-prod-sa.0.iam_email] - "roles/resourcemanager.projectCreator" = [module.branch-pf-prod-sa.0.iam_email] + "roles/owner" = local.branch_optional_sa_lists.pf-prod + "roles/logging.admin" = local.branch_optional_sa_lists.pf-prod + "roles/resourcemanager.folderAdmin" = local.branch_optional_sa_lists.pf-prod + "roles/resourcemanager.projectCreator" = local.branch_optional_sa_lists.pf-prod } tag_bindings = { environment = try( diff --git a/fast/stages/01-resman/main.tf b/fast/stages/01-resman/main.tf index c40957964f..0928498f2d 100644 --- a/fast/stages/01-resman/main.tf +++ b/fast/stages/01-resman/main.tf @@ -26,6 +26,12 @@ locals { billing_ext = var.billing_account.organization_id == null billing_org = var.billing_account.organization_id == var.organization.id billing_org_ext = !local.billing_ext && !local.billing_org + branch_optional_sa_lists = { + dp-dev = compact([try(module.branch-dp-dev-sa.0.iam_email, "")]) + dp-prod = compact([try(module.branch-dp-prod-sa.0.iam_email, "")]) + pf-dev = compact([try(module.branch-pf-dev-sa.0.iam_email, "")]) + pf-prod = compact([try(module.branch-pf-prod-sa.0.iam_email, "")]) + } cicd_repositories = { for k, v in coalesce(var.cicd_repositories, {}) : k => v if( @@ -57,7 +63,6 @@ locals { "02-security.auto.tfvars.json" ] } - custom_roles = coalesce(var.custom_roles, {}) groups = { for k, v in var.groups : diff --git a/fast/stages/01-resman/organization.tf b/fast/stages/01-resman/organization.tf index 1c012caadf..da89528aad 100644 --- a/fast/stages/01-resman/organization.tf +++ b/fast/stages/01-resman/organization.tf @@ -18,23 +18,6 @@ locals { - branch_dataplatform_sa_iam_emails = ( - var.fast_features.data_platform - ? [ - module.branch-dp-dev-sa.0.iam_email, - module.branch-dp-prod-sa.0.iam_email - ] - : [] - ) - # set to the empty list if you remove the teams branch - branch_teams_pf_sa_iam_emails = ( - var.fast_features.project_factory - ? [ - module.branch-pf-dev-sa.0.iam_email, - module.branch-pf-prod-sa.0.iam_email - ] - : [] - ) list_allow = { inherit_from_parent = false suggested_value = null @@ -72,18 +55,19 @@ module "organization" { ] }, local.billing_org ? { - "roles/billing.costsManager" = local.branch_teams_pf_sa_iam_emails + "roles/billing.costsManager" = concat( + local.branch_optional_sa_lists.pf-dev, + local.branch_optional_sa_lists.pf-prod + ) "roles/billing.user" = concat( [ module.branch-network-sa.iam_email, module.branch-security-sa.iam_email, ], - local.branch_dataplatform_sa_iam_emails, - # enable if individual teams can create their own projects - # [ - # for k, v in module.branch-teams-team-sa : v.iam_email - # ], - local.branch_teams_pf_sa_iam_emails + local.branch_optional_sa_lists.dp-dev, + local.branch_optional_sa_lists.dp-prod, + local.branch_optional_sa_lists.pf-dev, + local.branch_optional_sa_lists.pf-prod, ) } : {} ) diff --git a/fast/stages/01-resman/outputs.tf b/fast/stages/01-resman/outputs.tf index 9e0b6a17ba..51f0aa3716 100644 --- a/fast/stages/01-resman/outputs.tf +++ b/fast/stages/01-resman/outputs.tf @@ -127,7 +127,24 @@ locals { name = "sandbox" sa = module.branch-sandbox-sa.0.email }) - } + }, + !var.fast_features.teams ? {} : merge( + { + "03-teams" = templatefile(local._tpl_providers, { + 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 : + "03-teams-${k}" => templatefile(local._tpl_providers, { + bucket = module.branch-teams-team-gcs[k].name + name = "teams" + sa = v.email + }) + } + ) ) service_accounts = merge( { @@ -138,7 +155,7 @@ locals { project-factory-prod = try(module.branch-pf-prod-sa.0.email, null) sandbox = try(module.branch-sandbox-sa.0.email, null) security = module.branch-security-sa.email - teams = try(module.branch-teams-prod-sa.0.email, null) + teams = try(module.branch-teams-sa.0.email, null) }, { for k, v in module.branch-teams-team-sa : "team-${k}" => v.email