From 66c4fffd76789bac32849bcba075401c88b16a7d Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Tue, 28 Jun 2022 17:33:37 +0200 Subject: [PATCH] FAST: configuration switches for features (#703) * example implementation of top-level switches * data platform as a fast feature * decouple teams and project factory * teams disable fixes * optional pf * networking stage * remove var from stage 2s, security --- fast/stages/00-bootstrap/README.md | 37 ++--- fast/stages/00-bootstrap/outputs.tf | 1 + fast/stages/00-bootstrap/variables.tf | 17 ++ fast/stages/00-cicd/README.md | 10 +- fast/stages/01-resman/README.md | 36 +++-- fast/stages/01-resman/branch-data-platform.tf | 72 +++++++-- fast/stages/01-resman/branch-networking.tf | 16 +- .../01-resman/branch-project-factory.tf | 97 +++++++++++ fast/stages/01-resman/branch-sandbox.tf | 28 +++- fast/stages/01-resman/branch-teams.tf | 104 ++++-------- fast/stages/01-resman/cicd-data-platform.tf | 10 +- ...{cicd-teams.tf => cicd-project-factory.tf} | 44 +++-- fast/stages/01-resman/organization.tf | 55 +++++-- fast/stages/01-resman/outputs.tf | 151 ++++++++++-------- fast/stages/01-resman/variables.tf | 18 +++ fast/stages/02-networking-nva/spoke-dev.tf | 6 +- fast/stages/02-networking-nva/spoke-prod.tf | 6 +- .../stages/02-networking-peering/spoke-dev.tf | 6 +- .../02-networking-peering/spoke-prod.tf | 6 +- fast/stages/02-networking-vpn/spoke-dev.tf | 6 +- fast/stages/02-networking-vpn/spoke-prod.tf | 6 +- fast/stages/02-security/core-dev.tf | 6 +- fast/stages/02-security/core-prod.tf | 6 +- 23 files changed, 478 insertions(+), 266 deletions(-) create mode 100644 fast/stages/01-resman/branch-project-factory.tf rename fast/stages/01-resman/{cicd-teams.tf => cicd-project-factory.tf} (82%) diff --git a/fast/stages/00-bootstrap/README.md b/fast/stages/00-bootstrap/README.md index 7280a34b..5b7f495d 100644 --- a/fast/stages/00-bootstrap/README.md +++ b/fast/stages/00-bootstrap/README.md @@ -461,31 +461,32 @@ The remaining configuration is manual, as it regards the repositories themselves | name | description | type | required | default | producer | |---|---|:---:|:---:|:---:|:---:| | [billing_account](variables.tf#L17) | Billing account id and organization id ('nnnnnnnn' or null). | object({…}) | ✓ | | | -| [organization](variables.tf#L162) | Organization details. | object({…}) | ✓ | | | -| [prefix](variables.tf#L177) | Prefix used for resources that need unique names. Use 9 characters or less. | string | ✓ | | | +| [organization](variables.tf#L179) | Organization details. | object({…}) | ✓ | | | +| [prefix](variables.tf#L194) | Prefix used for resources that need unique names. Use 9 characters or less. | string | ✓ | | | | [bootstrap_user](variables.tf#L25) | Email of the nominal user running this stage for the first time. | string | | null | | | [cicd_repositories](variables.tf#L31) | CI/CD repository configuration. Identity providers reference keys in the `federated_identity_providers` variable. Set to null to disable, or set individual repositories to null if not needed. | object({…}) | | null | | | [custom_role_names](variables.tf#L83) | Names of custom roles defined at the org level. | object({…}) | | {…} | | -| [federated_identity_providers](variables.tf#L95) | Workload Identity Federation pools. The `cicd_repositories` variable references keys here. | map(object({…})) | | {} | | -| [groups](variables.tf#L109) | Group names to grant organization-level permissions. | map(string) | | {…} | | -| [iam](variables.tf#L123) | Organization-level custom IAM settings in role => [principal] format. | map(list(string)) | | {} | | -| [iam_additive](variables.tf#L129) | Organization-level custom IAM settings in role => [principal] format for non-authoritative bindings. | map(list(string)) | | {} | | -| [log_sinks](variables.tf#L137) | Org-level log sinks, in name => {type, filter} format. | map(object({…})) | | {…} | | -| [outputs_location](variables.tf#L171) | Enable writing provider, tfvars and CI/CD workflow files to local filesystem. Leave null to disable | string | | null | | +| [fast_features](variables.tf#L95) | Selective control for top-level FAST features. | object({…}) | | {…} | | +| [federated_identity_providers](variables.tf#L112) | Workload Identity Federation pools. The `cicd_repositories` variable references keys here. | map(object({…})) | | {} | | +| [groups](variables.tf#L126) | Group names to grant organization-level permissions. | map(string) | | {…} | | +| [iam](variables.tf#L140) | Organization-level custom IAM settings in role => [principal] format. | map(list(string)) | | {} | | +| [iam_additive](variables.tf#L146) | Organization-level custom IAM settings in role => [principal] format for non-authoritative bindings. | map(list(string)) | | {} | | +| [log_sinks](variables.tf#L154) | Org-level log sinks, in name => {type, filter} format. | map(object({…})) | | {…} | | +| [outputs_location](variables.tf#L188) | Enable writing provider, tfvars and CI/CD workflow files to local filesystem. Leave null to disable | string | | null | | ## Outputs | name | description | sensitive | consumers | |---|---|:---:|---| -| [automation](outputs.tf#L87) | Automation resources. | | | -| [billing_dataset](outputs.tf#L92) | BigQuery dataset prepared for billing export. | | | -| [cicd_repositories](outputs.tf#L97) | CI/CD repository configurations. | | | -| [custom_roles](outputs.tf#L109) | Organization-level custom roles. | | | -| [federated_identity](outputs.tf#L114) | Workload Identity Federation pool and providers. | | | -| [outputs_bucket](outputs.tf#L124) | GCS bucket where generated output files are stored. | | | -| [project_ids](outputs.tf#L129) | Projects created by this stage. | | | -| [providers](outputs.tf#L149) | Terraform provider files for this stage and dependent stages. | ✓ | stage-01 | -| [service_accounts](outputs.tf#L138) | Automation service accounts created by this stage. | | | -| [tfvars](outputs.tf#L158) | Terraform variable files for the following stages. | ✓ | | +| [automation](outputs.tf#L88) | Automation resources. | | | +| [billing_dataset](outputs.tf#L93) | BigQuery dataset prepared for billing export. | | | +| [cicd_repositories](outputs.tf#L98) | CI/CD repository configurations. | | | +| [custom_roles](outputs.tf#L110) | Organization-level custom roles. | | | +| [federated_identity](outputs.tf#L115) | Workload Identity Federation pool and providers. | | | +| [outputs_bucket](outputs.tf#L125) | GCS bucket where generated output files are stored. | | | +| [project_ids](outputs.tf#L130) | Projects created by this stage. | | | +| [providers](outputs.tf#L150) | Terraform provider files for this stage and dependent stages. | ✓ | stage-01 | +| [service_accounts](outputs.tf#L139) | Automation service accounts created by this stage. | | | +| [tfvars](outputs.tf#L159) | Terraform variable files for the following stages. | ✓ | | diff --git a/fast/stages/00-bootstrap/outputs.tf b/fast/stages/00-bootstrap/outputs.tf index eceb5f82..28e0e4d7 100644 --- a/fast/stages/00-bootstrap/outputs.tf +++ b/fast/stages/00-bootstrap/outputs.tf @@ -68,6 +68,7 @@ locals { } tfvars_globals = { billing_account = var.billing_account + fast_features = var.fast_features groups = var.groups organization = var.organization prefix = var.prefix diff --git a/fast/stages/00-bootstrap/variables.tf b/fast/stages/00-bootstrap/variables.tf index f2f1edcd..e9cc26e3 100644 --- a/fast/stages/00-bootstrap/variables.tf +++ b/fast/stages/00-bootstrap/variables.tf @@ -92,6 +92,23 @@ variable "custom_role_names" { } } +variable "fast_features" { + description = "Selective control for top-level FAST features." + type = object({ + data_platform = bool + project_factory = bool + sandbox = bool + teams = bool + }) + default = { + data_platform = true + project_factory = true + sandbox = true + teams = true + } + nullable = false +} + variable "federated_identity_providers" { description = "Workload Identity Federation pools. The `cicd_repositories` variable references keys here." type = map(object({ diff --git a/fast/stages/00-cicd/README.md b/fast/stages/00-cicd/README.md index 54c2b724..a0b92733 100644 --- a/fast/stages/00-cicd/README.md +++ b/fast/stages/00-cicd/README.md @@ -1,11 +1,10 @@ # CI/CD bootstrap -The primary purpose of this stage is to set up your CI/CD project structure automatically, with most of the -necessary configuration to run the pipelines out of the box. +The primary purpose of this stage is to set up your CI/CD project structure automatically, with most of the necessary configuration to run the pipelines out of the box. ## How to run this stage -This stage is meant to be executed after the [bootstrap](../00-bootstrap) stage has run, as it leverages the automation service account and bucket created there. +This stage is meant to be executed after the [bootstrap](../00-bootstrap) stage has run, as it leverages the automation service account and bucket created there. The entire stage is optional, you may also choose to create your repositories manually. ### Providers configuration @@ -51,7 +50,7 @@ cp ../00-bootstrap/terraform.tfvars . A second set of variables is specific to this stage, they are all optional so if you need to customize them, create an extra `terraform.tfvars` file or add them to the file copied from bootstrap. -Refer to the [Variables](#variables) table at the bottom of this document, for a full list of variables, their origin (e.g. a stage or specific to this one), and descriptions explaining their meaning. The sections below also describe some of the possible customizations. +Refer to the [Variables](#variables) table at the bottom of this document, for a full list of variables, their origin (e.g. a stage or specific to this one), and descriptions explaining their meaning. The sections below also describe some of the possible customizations. ### CI/CD systems @@ -89,7 +88,7 @@ and such, the `00-cicd` stage creates all the repositories in your CI/CD system configuration is essentially a combination of all the `cicd_repositories` variables of the other stages plus additional CI/CD system specific configuration information. -This is an example of configuring the repositories in this stage. +This is an example of configuring the repositories in this stage. ```hcl cicd_repositories = { @@ -163,7 +162,6 @@ The `type` attribute can be set to one of the supported repository types: `githu 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. - Once done, you can run this stage: ```bash diff --git a/fast/stages/01-resman/README.md b/fast/stages/01-resman/README.md index a054106f..60e429d7 100644 --- a/fast/stages/01-resman/README.md +++ b/fast/stages/01-resman/README.md @@ -160,13 +160,14 @@ Due to its simplicity, this stage lends itself easily to customizations: adding | [billing.tf](./billing.tf) | Billing resources for external billing use cases. | organization | google_billing_account_iam_member | | [branch-data-platform.tf](./branch-data-platform.tf) | Data Platform stages resources. | folder · gcs · iam-service-account | | | [branch-networking.tf](./branch-networking.tf) | Networking stage resources. | folder · gcs · iam-service-account | | +| [branch-project-factory.tf](./branch-project-factory.tf) | Project factory stage resources. | gcs · iam-service-account | | | [branch-sandbox.tf](./branch-sandbox.tf) | Sandbox stage resources. | folder · gcs · iam-service-account | | | [branch-security.tf](./branch-security.tf) | Security stage resources. | folder · gcs · iam-service-account | | | [branch-teams.tf](./branch-teams.tf) | Team stage resources. | folder · gcs · iam-service-account | | | [cicd-data-platform.tf](./cicd-data-platform.tf) | CI/CD resources for the data platform branch. | iam-service-account · source-repository | | | [cicd-networking.tf](./cicd-networking.tf) | CI/CD resources for the networking branch. | iam-service-account · source-repository | | +| [cicd-project-factory.tf](./cicd-project-factory.tf) | CI/CD resources for the teams branch. | iam-service-account · source-repository | | | [cicd-security.tf](./cicd-security.tf) | CI/CD resources for the security branch. | iam-service-account · source-repository | | -| [cicd-teams.tf](./cicd-teams.tf) | CI/CD resources for the teams branch. | iam-service-account · source-repository | | | [main.tf](./main.tf) | Module-level locals and resources. | | | | [organization.tf](./organization.tf) | Organization policies. | organization | google_organization_iam_member | | [outputs-files.tf](./outputs-files.tf) | Output files persistence to local filesystem. | | local_file | @@ -180,28 +181,29 @@ Due to its simplicity, this stage lends itself easily to customizations: adding |---|---|:---:|:---:|:---:|:---:| | [automation](variables.tf#L20) | Automation resources created by the bootstrap stage. | object({…}) | ✓ | | 00-bootstrap | | [billing_account](variables.tf#L38) | Billing account id and organization id ('nnnnnnnn' or null). | object({…}) | ✓ | | 00-bootstrap | -| [organization](variables.tf#L141) | Organization details. | object({…}) | ✓ | | 00-bootstrap | -| [prefix](variables.tf#L165) | Prefix used for resources that need unique names. Use 9 characters or less. | string | ✓ | | 00-bootstrap | +| [organization](variables.tf#L159) | Organization details. | object({…}) | ✓ | | 00-bootstrap | +| [prefix](variables.tf#L183) | Prefix used for resources that need unique names. Use 9 characters or less. | string | ✓ | | 00-bootstrap | | [cicd_repositories](variables.tf#L47) | CI/CD repository configuration. Identity providers reference keys in the `automation.federated_identity_providers` variable. Set to null to disable, or set individual repositories to null if not needed. | object({…}) | | null | | | [custom_roles](variables.tf#L117) | Custom roles defined at the org level, in key => id format. | object({…}) | | null | 00-bootstrap | -| [groups](variables.tf#L126) | Group names to grant organization-level permissions. | map(string) | | {…} | 00-bootstrap | -| [organization_policy_configs](variables.tf#L151) | Organization policies customization. | object({…}) | | null | | -| [outputs_location](variables.tf#L159) | Enable writing provider, tfvars and CI/CD workflow files to local filesystem. Leave null to disable | string | | null | | -| [tag_names](variables.tf#L176) | Customized names for resource management tags. | object({…}) | | {…} | | -| [team_folders](variables.tf#L193) | Team folders to be created. Format is described in a code comment. | map(object({…})) | | null | | +| [fast_features](variables.tf#L126) | Selective control for top-level FAST features. | object({…}) | | {…} | 00-bootstrap | +| [groups](variables.tf#L144) | Group names to grant organization-level permissions. | map(string) | | {…} | 00-bootstrap | +| [organization_policy_configs](variables.tf#L169) | Organization policies customization. | object({…}) | | null | | +| [outputs_location](variables.tf#L177) | Enable writing provider, tfvars and CI/CD workflow files to local filesystem. Leave null to disable | string | | null | | +| [tag_names](variables.tf#L194) | Customized names for resource management tags. | object({…}) | | {…} | | +| [team_folders](variables.tf#L211) | Team folders to be created. Format is described in a code comment. | map(object({…})) | | null | | ## Outputs | name | description | sensitive | consumers | |---|---|:---:|---| -| [cicd_repositories](outputs.tf#L145) | WIF configuration for CI/CD repositories. | | | -| [dataplatform](outputs.tf#L159) | Data for the Data Platform stage. | | | -| [networking](outputs.tf#L175) | Data for the networking stage. | | | -| [project_factories](outputs.tf#L184) | Data for the project factories stage. | | | -| [providers](outputs.tf#L200) | Terraform provider files for this stage and dependent stages. | ✓ | 02-networking · 02-security · 03-dataplatform · xx-sandbox · xx-teams | -| [sandbox](outputs.tf#L207) | Data for the sandbox stage. | | xx-sandbox | -| [security](outputs.tf#L217) | Data for the networking stage. | | 02-security | -| [teams](outputs.tf#L227) | Data for the teams stage. | | | -| [tfvars](outputs.tf#L240) | Terraform variable files for the following stages. | ✓ | | +| [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. | ✓ | | diff --git a/fast/stages/01-resman/branch-data-platform.tf b/fast/stages/01-resman/branch-data-platform.tf index 84e6d81e..237f8fb1 100644 --- a/fast/stages/01-resman/branch-data-platform.tf +++ b/fast/stages/01-resman/branch-data-platform.tf @@ -16,8 +16,14 @@ # tfdoc:file:description Data Platform stages resources. +moved { + from = module.branch-dp-folder + to = module.branch-dp-folder.0 +} + module "branch-dp-folder" { source = "../../../modules/folder" + count = var.fast_features.data_platform ? 1 : 0 parent = "organizations/${var.organization.id}" name = "Data Platform" tag_bindings = { @@ -27,18 +33,26 @@ module "branch-dp-folder" { } } +moved { + from = module.branch-dp-dev-folder + to = module.branch-dp-dev-folder.0 +} + module "branch-dp-dev-folder" { source = "../../../modules/folder" - parent = module.branch-dp-folder.id + count = var.fast_features.data_platform ? 1 : 0 + parent = module.branch-dp-folder.0.id name = "Development" group_iam = {} iam = { - (local.custom_roles.service_project_network_admin) = [module.branch-dp-dev-sa.iam_email] + (local.custom_roles.service_project_network_admin) = [ + module.branch-dp-dev-sa.0.iam_email + ] # remove owner here and at project level if SA does not manage project resources - "roles/owner" = [module.branch-dp-dev-sa.iam_email] - "roles/logging.admin" = [module.branch-dp-dev-sa.iam_email] - "roles/resourcemanager.folderAdmin" = [module.branch-dp-dev-sa.iam_email] - "roles/resourcemanager.projectCreator" = [module.branch-dp-dev-sa.iam_email] + "roles/owner" = [module.branch-dp-dev-sa.0.iam_email] + "roles/logging.admin" = [module.branch-dp-dev-sa.0.iam_email] + "roles/resourcemanager.folderAdmin" = [module.branch-dp-dev-sa.0.iam_email] + "roles/resourcemanager.projectCreator" = [module.branch-dp-dev-sa.0.iam_email] } tag_bindings = { context = try( @@ -47,18 +61,24 @@ module "branch-dp-dev-folder" { } } +moved { + from = module.branch-dp-prod-folder + to = module.branch-dp-prod-folder.0 +} + module "branch-dp-prod-folder" { source = "../../../modules/folder" - parent = module.branch-dp-folder.id + count = var.fast_features.data_platform ? 1 : 0 + parent = module.branch-dp-folder.0.id name = "Production" group_iam = {} iam = { - (local.custom_roles.service_project_network_admin) = [module.branch-dp-prod-sa.iam_email] + (local.custom_roles.service_project_network_admin) = [module.branch-dp-prod-sa.0.iam_email] # remove owner here and at project level if SA does not manage project resources - "roles/owner" = [module.branch-dp-prod-sa.iam_email] - "roles/logging.admin" = [module.branch-dp-prod-sa.iam_email] - "roles/resourcemanager.folderAdmin" = [module.branch-dp-prod-sa.iam_email] - "roles/resourcemanager.projectCreator" = [module.branch-dp-prod-sa.iam_email] + "roles/owner" = [module.branch-dp-prod-sa.0.iam_email] + "roles/logging.admin" = [module.branch-dp-prod-sa.0.iam_email] + "roles/resourcemanager.folderAdmin" = [module.branch-dp-prod-sa.0.iam_email] + "roles/resourcemanager.projectCreator" = [module.branch-dp-prod-sa.0.iam_email] } tag_bindings = { context = try( @@ -69,8 +89,14 @@ module "branch-dp-prod-folder" { # automation service accounts and buckets +moved { + from = module.branch-dp-dev-sa + to = module.branch-dp-dev-sa.0 +} + module "branch-dp-dev-sa" { source = "../../../modules/iam-service-account" + count = var.fast_features.data_platform ? 1 : 0 project_id = var.automation.project_id name = "dev-resman-dp-0" description = "Terraform data platform development service account." @@ -85,8 +111,14 @@ module "branch-dp-dev-sa" { } } +moved { + from = module.branch-dp-prod-sa + to = module.branch-dp-prod-sa.0 +} + module "branch-dp-prod-sa" { source = "../../../modules/iam-service-account" + count = var.fast_features.data_platform ? 1 : 0 project_id = var.automation.project_id name = "prod-resman-dp-0" description = "Terraform data platform production service account." @@ -101,24 +133,36 @@ module "branch-dp-prod-sa" { } } +moved { + from = module.branch-dp-dev-gcs + to = module.branch-dp-dev-gcs.0 +} + module "branch-dp-dev-gcs" { source = "../../../modules/gcs" + count = var.fast_features.data_platform ? 1 : 0 project_id = var.automation.project_id name = "dev-resman-dp-0" prefix = var.prefix versioning = true iam = { - "roles/storage.objectAdmin" = [module.branch-dp-dev-sa.iam_email] + "roles/storage.objectAdmin" = [module.branch-dp-dev-sa.0.iam_email] } } +moved { + from = module.branch-dp-prod-gcs + to = module.branch-dp-prod-gcs.0 +} + module "branch-dp-prod-gcs" { source = "../../../modules/gcs" + count = var.fast_features.data_platform ? 1 : 0 project_id = var.automation.project_id name = "prod-resman-dp-0" prefix = var.prefix versioning = true iam = { - "roles/storage.objectAdmin" = [module.branch-dp-prod-sa.iam_email] + "roles/storage.objectAdmin" = [module.branch-dp-prod-sa.0.iam_email] } } diff --git a/fast/stages/01-resman/branch-networking.tf b/fast/stages/01-resman/branch-networking.tf index 8757e7f8..684079fa 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" = [ - module.branch-dp-prod-sa.iam_email, - module.branch-teams-prod-pf-sa.iam_email - ] + "roles/compute.xpnAdmin" = compact([ + try(module.branch-dp-prod-sa.0.iam_email, ""), + try(module.branch-pf-prod-sa.0.iam_email, ""), + ]) } 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) = [ - module.branch-dp-dev-sa.iam_email, - module.branch-teams-dev-pf-sa.iam_email - ] + (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, ""), + ]) } tag_bindings = { environment = try( diff --git a/fast/stages/01-resman/branch-project-factory.tf b/fast/stages/01-resman/branch-project-factory.tf new file mode 100644 index 00000000..819fbcb8 --- /dev/null +++ b/fast/stages/01-resman/branch-project-factory.tf @@ -0,0 +1,97 @@ +/** + * Copyright 2022 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. + */ + +# tfdoc:file:description Project factory stage resources. + +moved { + from = module.branch-teams-dev-pf-sa + to = module.branch-pf-dev-sa.0 +} + +module "branch-pf-dev-sa" { + source = "../../../modules/iam-service-account" + count = var.fast_features.project_factory ? 1 : 0 + project_id = var.automation.project_id + name = "dev-resman-pf-0" + # naming: environment in description + description = "Terraform project factory development service account." + prefix = var.prefix + iam = { + "roles/iam.serviceAccountTokenCreator" = compact([ + try(module.branch-pf-dev-sa-cicd.0.iam_email, null) + ]) + } + iam_storage_roles = { + (var.automation.outputs_bucket) = ["roles/storage.admin"] + } +} + +moved { + from = module.branch-teams-prod-pf-sa + to = module.branch-pf-prod-sa.0 +} + +module "branch-pf-prod-sa" { + source = "../../../modules/iam-service-account" + count = var.fast_features.project_factory ? 1 : 0 + project_id = var.automation.project_id + name = "prod-resman-pf-0" + # naming: environment in description + description = "Terraform project factory production service account." + prefix = var.prefix + iam = { + "roles/iam.serviceAccountTokenCreator" = compact([ + try(module.branch-pf-prod-sa-cicd.0.iam_email, null) + ]) + } + iam_storage_roles = { + (var.automation.outputs_bucket) = ["roles/storage.admin"] + } +} + +moved { + from = module.branch-teams-dev-pf-gcs + to = module.branch-pf-dev-gcs.0 +} + +module "branch-pf-dev-gcs" { + source = "../../../modules/gcs" + count = var.fast_features.project_factory ? 1 : 0 + project_id = var.automation.project_id + name = "dev-resman-pf-0" + prefix = var.prefix + versioning = true + iam = { + "roles/storage.objectAdmin" = [module.branch-pf-dev-sa.0.iam_email] + } +} + +moved { + from = module.branch-teams-prod-pf-gcs + to = module.branch-pf-prod-gcs.0 +} + +module "branch-pf-prod-gcs" { + source = "../../../modules/gcs" + count = var.fast_features.project_factory ? 1 : 0 + project_id = var.automation.project_id + name = "prod-resman-pf-0" + prefix = var.prefix + versioning = true + iam = { + "roles/storage.objectAdmin" = [module.branch-pf-prod-sa.0.iam_email] + } +} diff --git a/fast/stages/01-resman/branch-sandbox.tf b/fast/stages/01-resman/branch-sandbox.tf index f2ba0bfb..06624ec7 100644 --- a/fast/stages/01-resman/branch-sandbox.tf +++ b/fast/stages/01-resman/branch-sandbox.tf @@ -16,15 +16,21 @@ # tfdoc:file:description Sandbox stage resources. +moved { + from = module.branch-sandbox-folder + to = module.branch-sandbox-folder.0 +} + module "branch-sandbox-folder" { source = "../../../modules/folder" + count = var.fast_features.sandbox ? 1 : 0 parent = "organizations/${var.organization.id}" name = "Sandbox" iam = { - "roles/logging.admin" = [module.branch-sandbox-sa.iam_email] - "roles/owner" = [module.branch-sandbox-sa.iam_email] - "roles/resourcemanager.folderAdmin" = [module.branch-sandbox-sa.iam_email] - "roles/resourcemanager.projectCreator" = [module.branch-sandbox-sa.iam_email] + "roles/logging.admin" = [module.branch-sandbox-sa.0.iam_email] + "roles/owner" = [module.branch-sandbox-sa.0.iam_email] + "roles/resourcemanager.folderAdmin" = [module.branch-sandbox-sa.0.iam_email] + "roles/resourcemanager.projectCreator" = [module.branch-sandbox-sa.0.iam_email] } policy_boolean = { "constraints/sql.restrictPublicIp" = false @@ -44,19 +50,31 @@ module "branch-sandbox-folder" { } } +moved { + from = module.branch-sandbox-gcs + to = module.branch-sandbox-gcs.0 +} + module "branch-sandbox-gcs" { source = "../../../modules/gcs" + count = var.fast_features.sandbox ? 1 : 0 project_id = var.automation.project_id name = "dev-resman-sbox-0" prefix = var.prefix versioning = true iam = { - "roles/storage.objectAdmin" = [module.branch-sandbox-sa.iam_email] + "roles/storage.objectAdmin" = [module.branch-sandbox-sa.0.iam_email] } } +moved { + from = module.branch-sandbox-sa + to = module.branch-sandbox-sa.0 +} + module "branch-sandbox-sa" { source = "../../../modules/iam-service-account" + count = var.fast_features.sandbox ? 1 : 0 project_id = var.automation.project_id name = "dev-resman-sbox-0" description = "Terraform resman sandbox service account." diff --git a/fast/stages/01-resman/branch-teams.tf b/fast/stages/01-resman/branch-teams.tf index 465255be..3c9a2d69 100644 --- a/fast/stages/01-resman/branch-teams.tf +++ b/fast/stages/01-resman/branch-teams.tf @@ -16,8 +16,14 @@ # tfdoc:file:description Team stage resources. +moved { + from = module.branch-teams-folder + to = module.branch-teams-folder.0 +} + module "branch-teams-folder" { source = "../../../modules/folder" + count = var.fast_features.teams ? 1 : 0 parent = "organizations/${var.organization.id}" name = "Teams" tag_bindings = { @@ -27,8 +33,14 @@ module "branch-teams-folder" { } } +moved { + from = module.branch-teams-prod-sa + to = module.branch-teams-prod-sa.0 +} + module "branch-teams-prod-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." @@ -39,15 +51,15 @@ module "branch-teams-prod-sa" { module "branch-teams-team-folder" { source = "../../../modules/folder" - for_each = coalesce(var.team_folders, {}) - parent = module.branch-teams-folder.id + for_each = var.fast_features.teams ? coalesce(var.team_folders, {}) : {} + parent = module.branch-teams-folder.0.id name = each.value.descriptive_name group_iam = each.value.group_iam == null ? {} : each.value.group_iam } module "branch-teams-team-sa" { source = "../../../modules/iam-service-account" - for_each = coalesce(var.team_folders, {}) + for_each = var.fast_features.teams ? coalesce(var.team_folders, {}) : {} project_id = var.automation.project_id name = "prod-teams-${each.key}-0" description = "Terraform team ${each.key} service account." @@ -63,7 +75,7 @@ module "branch-teams-team-sa" { module "branch-teams-team-gcs" { source = "../../../modules/gcs" - for_each = coalesce(var.team_folders, {}) + for_each = var.fast_features.teams ? coalesce(var.team_folders, {}) : {} project_id = var.automation.project_id name = "prod-teams-${each.key}-0" prefix = var.prefix @@ -77,19 +89,19 @@ module "branch-teams-team-gcs" { module "branch-teams-team-dev-folder" { source = "../../../modules/folder" - for_each = coalesce(var.team_folders, {}) + for_each = var.fast_features.teams ? coalesce(var.team_folders, {}) : {} parent = module.branch-teams-team-folder[each.key].id # naming: environment descriptive name name = "Development" # environment-wide human permissions on the whole teams environment group_iam = {} iam = { - (local.custom_roles.service_project_network_admin) = [module.branch-teams-dev-pf-sa.iam_email] + (local.custom_roles.service_project_network_admin) = [module.branch-pf-dev-sa.0.iam_email] # remove owner here and at project level if SA does not manage project resources - "roles/owner" = [module.branch-teams-dev-pf-sa.iam_email] - "roles/logging.admin" = [module.branch-teams-dev-pf-sa.iam_email] - "roles/resourcemanager.folderAdmin" = [module.branch-teams-dev-pf-sa.iam_email] - "roles/resourcemanager.projectCreator" = [module.branch-teams-dev-pf-sa.iam_email] + "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] } tag_bindings = { environment = try( @@ -100,19 +112,19 @@ module "branch-teams-team-dev-folder" { module "branch-teams-team-prod-folder" { source = "../../../modules/folder" - for_each = coalesce(var.team_folders, {}) + for_each = var.fast_features.teams ? coalesce(var.team_folders, {}) : {} parent = module.branch-teams-team-folder[each.key].id # naming: environment descriptive name name = "Production" # environment-wide human permissions on the whole teams environment group_iam = {} iam = { - (local.custom_roles.service_project_network_admin) = [module.branch-teams-prod-pf-sa.iam_email] + (local.custom_roles.service_project_network_admin) = [module.branch-pf-prod-sa.0.iam_email] # remove owner here and at project level if SA does not manage project resources - "roles/owner" = [module.branch-teams-prod-pf-sa.iam_email] - "roles/logging.admin" = [module.branch-teams-prod-pf-sa.iam_email] - "roles/resourcemanager.folderAdmin" = [module.branch-teams-prod-pf-sa.iam_email] - "roles/resourcemanager.projectCreator" = [module.branch-teams-prod-pf-sa.iam_email] + "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] } tag_bindings = { environment = try( @@ -120,63 +132,3 @@ module "branch-teams-team-prod-folder" { ) } } - -# project factory per-team environment service accounts - -module "branch-teams-dev-pf-sa" { - source = "../../../modules/iam-service-account" - project_id = var.automation.project_id - name = "dev-resman-pf-0" - # naming: environment in description - description = "Terraform project factory development service account." - prefix = var.prefix - iam = { - "roles/iam.serviceAccountTokenCreator" = compact([ - try(module.branch-teams-dev-pf-sa-cicd.0.iam_email, null) - ]) - } - iam_storage_roles = { - (var.automation.outputs_bucket) = ["roles/storage.admin"] - } -} - -module "branch-teams-prod-pf-sa" { - source = "../../../modules/iam-service-account" - project_id = var.automation.project_id - name = "prod-resman-pf-0" - # naming: environment in description - description = "Terraform project factory production service account." - prefix = var.prefix - iam = { - "roles/iam.serviceAccountTokenCreator" = compact([ - try(module.branch-teams-prod-pf-sa-cicd.0.iam_email, null) - ]) - } - iam_storage_roles = { - (var.automation.outputs_bucket) = ["roles/storage.admin"] - } -} - -# project factory per-team environment GCS buckets - -module "branch-teams-dev-pf-gcs" { - source = "../../../modules/gcs" - project_id = var.automation.project_id - name = "dev-resman-pf-0" - prefix = var.prefix - versioning = true - iam = { - "roles/storage.objectAdmin" = [module.branch-teams-dev-pf-sa.iam_email] - } -} - -module "branch-teams-prod-pf-gcs" { - source = "../../../modules/gcs" - project_id = var.automation.project_id - name = "prod-resman-pf-0" - prefix = var.prefix - versioning = true - iam = { - "roles/storage.objectAdmin" = [module.branch-teams-prod-pf-sa.iam_email] - } -} diff --git a/fast/stages/01-resman/cicd-data-platform.tf b/fast/stages/01-resman/cicd-data-platform.tf index 4a5c6d3f..51adb33b 100644 --- a/fast/stages/01-resman/cicd-data-platform.tf +++ b/fast/stages/01-resman/cicd-data-platform.tf @@ -28,8 +28,12 @@ module "branch-dp-dev-cicd-repo" { project_id = var.automation.project_id name = each.value.name iam = { - "roles/source.admin" = [module.branch-dp-dev-sa.iam_email] - "roles/source.reader" = [module.branch-dp-dev-sa-cicd.0.iam_email] + "roles/source.admin" = compact([ + try(module.branch-dp-dev-sa.0.iam_email, "") + ]) + "roles/source.reader" = compact([ + try(module.branch-dp-dev-sa-cicd.0.iam_email, "") + ]) } triggers = { fast-03-dp-dev = { @@ -60,7 +64,7 @@ module "branch-dp-prod-cicd-repo" { project_id = var.automation.project_id name = each.value.name iam = { - "roles/source.admin" = [module.branch-dp-prod-sa.iam_email] + "roles/source.admin" = [module.branch-dp-prod-sa.0.iam_email] "roles/source.reader" = [module.branch-dp-prod-sa-cicd.0.iam_email] } triggers = { diff --git a/fast/stages/01-resman/cicd-teams.tf b/fast/stages/01-resman/cicd-project-factory.tf similarity index 82% rename from fast/stages/01-resman/cicd-teams.tf rename to fast/stages/01-resman/cicd-project-factory.tf index f5e81fd3..62f36f9d 100644 --- a/fast/stages/01-resman/cicd-teams.tf +++ b/fast/stages/01-resman/cicd-project-factory.tf @@ -18,7 +18,12 @@ # source repositories -module "branch-teams-dev-pf-cicd-repo" { +moved { + from = module.branch-teams-dev-pf-cicd-repo + to = module.branch-pf-dev-cicd-repo +} + +module "branch-pf-dev-cicd-repo" { source = "../../../modules/source-repository" for_each = ( try(local.cicd_repositories.project_factory_dev.type, null) == "sourcerepo" @@ -28,8 +33,8 @@ module "branch-teams-dev-pf-cicd-repo" { project_id = var.automation.project_id name = each.value.name iam = { - "roles/source.admin" = [module.branch-teams-dev-pf-sa.iam_email] - "roles/source.reader" = [module.branch-teams-dev-pf-sa-cicd.0.iam_email] + "roles/source.admin" = [module.branch-pf-dev-sa.0.iam_email] + "roles/source.reader" = [module.branch-pf-dev-sa-cicd.0.iam_email] } triggers = { fast-03-pf-dev = { @@ -37,7 +42,7 @@ module "branch-teams-dev-pf-cicd-repo" { included_files = [ "**/*json", "**/*tf", "**/*yaml", ".cloudbuild/workflow.yaml" ] - service_account = module.branch-teams-dev-pf-sa-cicd.0.id + service_account = module.branch-pf-dev-sa-cicd.0.id substitutions = {} template = { project_id = null @@ -47,10 +52,15 @@ module "branch-teams-dev-pf-cicd-repo" { } } } - depends_on = [module.branch-teams-dev-pf-sa-cicd] + depends_on = [module.branch-pf-dev-sa-cicd] } -module "branch-teams-prod-pf-cicd-repo" { +moved { + from = module.branch-teams-prod-pf-cicd-repo + to = module.branch-pf-prod-cicd-repo +} + +module "branch-pf-prod-cicd-repo" { source = "../../../modules/source-repository" for_each = ( try(local.cicd_repositories.project_factory_prod.type, null) == "sourcerepo" @@ -60,8 +70,8 @@ module "branch-teams-prod-pf-cicd-repo" { project_id = var.automation.project_id name = each.value.name iam = { - "roles/source.admin" = [module.branch-teams-prod-pf-sa.iam_email] - "roles/source.reader" = [module.branch-teams-prod-pf-sa-cicd.0.iam_email] + "roles/source.admin" = [module.branch-pf-prod-sa.0.iam_email] + "roles/source.reader" = [module.branch-pf-prod-sa-cicd.0.iam_email] } triggers = { fast-03-pf-prod = { @@ -69,7 +79,7 @@ module "branch-teams-prod-pf-cicd-repo" { included_files = [ "**/*json", "**/*tf", "**/*yaml", ".cloudbuild/workflow.yaml" ] - service_account = module.branch-teams-prod-pf-sa-cicd.0.id + service_account = module.branch-pf-prod-sa-cicd.0.id substitutions = {} template = { project_id = null @@ -79,12 +89,17 @@ module "branch-teams-prod-pf-cicd-repo" { } } } - depends_on = [module.branch-teams-prod-pf-sa-cicd] + depends_on = [module.branch-pf-prod-sa-cicd] } # SAs used by CI/CD workflows to impersonate automation SAs -module "branch-teams-dev-pf-sa-cicd" { +moved { + from = module.branch-teams-dev-pf-sa-cicd + to = module.branch-pf-dev-sa-cicd +} + +module "branch-pf-dev-sa-cicd" { source = "../../../modules/iam-service-account" for_each = ( try(local.cicd_repositories.project_factory_dev.name, null) != null @@ -125,7 +140,12 @@ module "branch-teams-dev-pf-sa-cicd" { } } -module "branch-teams-prod-pf-sa-cicd" { +moved { + from = module.branch-teams-prod-pf-sa-cicd + to = module.branch-pf-prod-sa-cicd +} + +module "branch-pf-prod-sa-cicd" { source = "../../../modules/iam-service-account" for_each = ( try(local.cicd_repositories.project_factory_prod.name, null) != null diff --git a/fast/stages/01-resman/organization.tf b/fast/stages/01-resman/organization.tf index 6ce4e9ce..1c012caa 100644 --- a/fast/stages/01-resman/organization.tf +++ b/fast/stages/01-resman/organization.tf @@ -18,16 +18,23 @@ locals { - # set to the empty list if you remove the data platform branch - branch_dataplatform_sa_iam_emails = [ - module.branch-dp-dev-sa.iam_email, - module.branch-dp-prod-sa.iam_email - ] + 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 = [ - module.branch-teams-dev-pf-sa.iam_email, - module.branch-teams-prod-pf-sa.iam_email - ] + 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 @@ -176,18 +183,16 @@ module "organization" { # organization policy admin role assigned with a condition on tags -resource "google_organization_iam_member" "org_policy_admin" { - for_each = { - data-dev = ["data", "development", module.branch-dp-dev-sa.iam_email] - data-prod = ["data", "production", module.branch-dp-prod-sa.iam_email] - pf-dev = ["teams", "development", module.branch-teams-dev-pf-sa.iam_email] - pf-prod = ["teams", "production", module.branch-teams-prod-pf-sa.iam_email] +resource "google_organization_iam_member" "org_policy_admin_dp" { + for_each = !var.fast_features.data_platform ? {} : { + data-dev = ["data", "development", module.branch-dp-dev-sa.0.iam_email] + data-prod = ["data", "production", module.branch-dp-prod-sa.0.iam_email] } org_id = var.organization.id role = "roles/orgpolicy.policyAdmin" member = each.value.2 condition { - title = "org_policy_tag_scoped" + title = "org_policy_tag_dp_scoped" description = "Org policy tag scoped grant for ${each.value.0}/${each.value.1}." expression = <<-END resource.matchTag('${var.organization.id}/${var.tag_names.context}', '${each.value.0}') @@ -197,3 +202,21 @@ resource "google_organization_iam_member" "org_policy_admin" { } } +resource "google_organization_iam_member" "org_policy_admin_pf" { + for_each = !var.fast_features.project_factory ? {} : { + pf-dev = ["teams", "development", module.branch-pf-dev-sa.0.iam_email] + pf-prod = ["teams", "production", module.branch-pf-prod-sa.0.iam_email] + } + org_id = var.organization.id + role = "roles/orgpolicy.policyAdmin" + member = each.value.2 + condition { + title = "org_policy_tag_pf_scoped" + description = "Org policy tag scoped grant for ${each.value.0}/${each.value.1}." + expression = <<-END + resource.matchTag('${var.organization.id}/${var.tag_names.context}', '${each.value.0}') + && + resource.matchTag('${var.organization.id}/${var.tag_names.environment}', '${each.value.1}') + END + } +} diff --git a/fast/stages/01-resman/outputs.tf b/fast/stages/01-resman/outputs.tf index 73ed2eed..9e0b6a17 100644 --- a/fast/stages/01-resman/outputs.tf +++ b/fast/stages/01-resman/outputs.tf @@ -33,12 +33,12 @@ locals { tf_var_files = local.cicd_workflow_var_files.stage_2 } project_factory_dev = { - service_account = try(module.branch-teams-dev-pf-sa-cicd.0.email, null) + service_account = try(module.branch-pf-dev-sa-cicd.0.email, null) tf_providers_file = "03-project-factory-dev-providers.tf" tf_var_files = local.cicd_workflow_var_files.stage_3 } project_factory_prod = { - service_account = try(module.branch-teams-prod-pf-sa-cicd.0.email, null) + service_account = try(module.branch-pf-prod-sa-cicd.0.email, null) tf_providers_file = "03-project-factory-prod-providers.tf" tf_var_files = local.cicd_workflow_var_files.stage_3 } @@ -62,13 +62,14 @@ locals { } folder_ids = merge( { - data-platform = module.branch-dp-dev-folder.id - networking = module.branch-network-folder.id - networking-dev = module.branch-network-dev-folder.id - networking-prod = module.branch-network-prod-folder.id - sandbox = module.branch-sandbox-folder.id - security = module.branch-security-folder.id - teams = module.branch-teams-folder.id + data-platform-dev = try(module.branch-dp-dev-folder.0.id, null) + data-platform-prod = try(module.branch-dp-prod-folder.0.id, null) + networking = module.branch-network-folder.id + networking-dev = module.branch-network-dev-folder.id + networking-prod = module.branch-network-prod-folder.id + sandbox = try(module.branch-sandbox-folder.0.id, null) + security = module.branch-security-folder.id + teams = try(module.branch-teams-folder.0.id, null) }, { for k, v in module.branch-teams-team-folder : @@ -83,53 +84,61 @@ locals { "team-${k}-prod" => v.id } ) - providers = { - "02-networking" = templatefile(local._tpl_providers, { - bucket = module.branch-network-gcs.name - name = "networking" - sa = module.branch-network-sa.email - }) - "02-security" = templatefile(local._tpl_providers, { - bucket = module.branch-security-gcs.name - name = "security" - sa = module.branch-security-sa.email - }) - "03-data-platform-dev" = templatefile(local._tpl_providers, { - bucket = module.branch-dp-dev-gcs.name - name = "dp-dev" - sa = module.branch-dp-dev-sa.email - }) - "03-data-platform-prod" = templatefile(local._tpl_providers, { - bucket = module.branch-dp-prod-gcs.name - name = "dp-prod" - sa = module.branch-dp-prod-sa.email - }) - "03-project-factory-dev" = templatefile(local._tpl_providers, { - bucket = module.branch-teams-dev-pf-gcs.name - name = "team-dev" - sa = module.branch-teams-dev-pf-sa.email - }) - "03-project-factory-prod" = templatefile(local._tpl_providers, { - bucket = module.branch-teams-prod-pf-gcs.name - name = "team-prod" - sa = module.branch-teams-prod-pf-sa.email - }) - "99-sandbox" = templatefile(local._tpl_providers, { - bucket = module.branch-sandbox-gcs.name - name = "sandbox" - sa = module.branch-sandbox-sa.email - }) - } + providers = merge( + { + "02-networking" = templatefile(local._tpl_providers, { + bucket = module.branch-network-gcs.name + name = "networking" + sa = module.branch-network-sa.email + }) + "02-security" = templatefile(local._tpl_providers, { + bucket = module.branch-security-gcs.name + name = "security" + sa = module.branch-security-sa.email + }) + }, + !var.fast_features.data_platform ? {} : { + "03-data-platform-dev" = templatefile(local._tpl_providers, { + bucket = module.branch-dp-dev-gcs.0.name + name = "dp-dev" + sa = module.branch-dp-dev-sa.0.email + }) + "03-data-platform-prod" = templatefile(local._tpl_providers, { + bucket = module.branch-dp-prod-gcs.0.name + name = "dp-prod" + sa = module.branch-dp-prod-sa.0.email + }) + }, + !var.fast_features.project_factory ? {} : { + "03-project-factory-dev" = templatefile(local._tpl_providers, { + bucket = module.branch-pf-dev-gcs.0.name + name = "team-dev" + sa = module.branch-pf-dev-sa.0.email + }) + "03-project-factory-prod" = templatefile(local._tpl_providers, { + bucket = module.branch-pf-prod-gcs.0.name + name = "team-prod" + sa = module.branch-pf-prod-sa.0.email + }) + }, + !var.fast_features.sandbox ? {} : { + "99-sandbox" = templatefile(local._tpl_providers, { + bucket = module.branch-sandbox-gcs.0.name + name = "sandbox" + sa = module.branch-sandbox-sa.0.email + }) + } + ) service_accounts = merge( { - data-platform-dev = module.branch-dp-dev-sa.email - data-platform-prod = module.branch-dp-prod-sa.email + data-platform-dev = try(module.branch-dp-dev-sa.0.email, null) + data-platform-prod = try(module.branch-dp-prod-sa.0.email, null) networking = module.branch-network-sa.email - project-factory-dev = module.branch-teams-dev-pf-sa.email - project-factory-prod = module.branch-teams-prod-pf-sa.email - sandbox = module.branch-sandbox-sa.email + project-factory-dev = try(module.branch-pf-dev-sa.0.email, null) + 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 = module.branch-teams-prod-sa.email + teams = try(module.branch-teams-prod-sa.0.email, null) }, { for k, v in module.branch-teams-team-sa : "team-${k}" => v.email @@ -158,16 +167,16 @@ output "cicd_repositories" { output "dataplatform" { description = "Data for the Data Platform stage." - value = { + value = !var.fast_features.data_platform ? {} : { dev = { - folder = module.branch-dp-dev-folder.id - gcs_bucket = module.branch-dp-dev-gcs.name - service_account = module.branch-dp-dev-sa.email + folder = module.branch-dp-dev-folder.0.id + gcs_bucket = module.branch-dp-dev-gcs.0.name + service_account = module.branch-dp-dev-sa.0.email } prod = { - folder = module.branch-dp-prod-folder.id - gcs_bucket = module.branch-dp-prod-gcs.name - service_account = module.branch-dp-prod-sa.email + folder = module.branch-dp-prod-folder.0.id + gcs_bucket = module.branch-dp-prod-gcs.0.name + service_account = module.branch-dp-prod-sa.0.email } } } @@ -183,14 +192,14 @@ output "networking" { output "project_factories" { description = "Data for the project factories stage." - value = { + value = !var.fast_features.project_factory ? {} : { dev = { - bucket = module.branch-teams-dev-pf-gcs.name - sa = module.branch-teams-dev-pf-sa.email + bucket = module.branch-pf-dev-gcs.0.name + sa = module.branch-pf-dev-sa.0.email } prod = { - bucket = module.branch-teams-prod-pf-gcs.name - sa = module.branch-teams-prod-pf-sa.email + bucket = module.branch-pf-prod-gcs.0.name + sa = module.branch-pf-prod-sa.0.email } } } @@ -207,11 +216,15 @@ output "providers" { output "sandbox" { # tfdoc:output:consumers xx-sandbox description = "Data for the sandbox stage." - value = { - folder = module.branch-sandbox-folder.id - gcs_bucket = module.branch-sandbox-gcs.name - service_account = module.branch-sandbox-sa.email - } + value = ( + var.fast_features.sandbox + ? { + folder = module.branch-sandbox-folder.0.id + gcs_bucket = module.branch-sandbox-gcs.0.name + service_account = module.branch-sandbox-sa.0.email + } + : null + ) } output "security" { diff --git a/fast/stages/01-resman/variables.tf b/fast/stages/01-resman/variables.tf index c1d534bc..35a05efe 100644 --- a/fast/stages/01-resman/variables.tf +++ b/fast/stages/01-resman/variables.tf @@ -123,6 +123,24 @@ variable "custom_roles" { default = null } +variable "fast_features" { + # tfdoc:variable:source 00-bootstrap + description = "Selective control for top-level FAST features." + type = object({ + data_platform = bool + project_factory = bool + sandbox = bool + teams = bool + }) + default = { + data_platform = true + project_factory = true + sandbox = true + teams = true + } + # nullable = false +} + variable "groups" { # tfdoc:variable:source 00-bootstrap description = "Group names to grant organization-level permissions." diff --git a/fast/stages/02-networking-nva/spoke-dev.tf b/fast/stages/02-networking-nva/spoke-dev.tf index 843d544f..d6da279d 100644 --- a/fast/stages/02-networking-nva/spoke-dev.tf +++ b/fast/stages/02-networking-nva/spoke-dev.tf @@ -40,7 +40,7 @@ module "dev-spoke-project" { } metric_scopes = [module.landing-project.project_id] iam = { - "roles/dns.admin" = [local.service_accounts.project-factory-dev] + "roles/dns.admin" = compact([local.service_accounts.project-factory-dev]) } } @@ -123,10 +123,10 @@ module "peering-dev" { resource "google_project_iam_binding" "dev_spoke_project_iam_delegated" { project = module.dev-spoke-project.project_id role = "roles/resourcemanager.projectIamAdmin" - members = [ + members = compact([ local.service_accounts.data-platform-dev, local.service_accounts.project-factory-dev, - ] + ]) condition { title = "dev_stage3_sa_delegated_grants" description = "Development host project delegated grants." diff --git a/fast/stages/02-networking-nva/spoke-prod.tf b/fast/stages/02-networking-nva/spoke-prod.tf index d0a22dc9..6f0e4edb 100644 --- a/fast/stages/02-networking-nva/spoke-prod.tf +++ b/fast/stages/02-networking-nva/spoke-prod.tf @@ -40,7 +40,7 @@ module "prod-spoke-project" { } metric_scopes = [module.landing-project.project_id] iam = { - "roles/dns.admin" = [local.service_accounts.project-factory-prod] + "roles/dns.admin" = compact([local.service_accounts.project-factory-prod]) } } @@ -123,10 +123,10 @@ module "peering-prod" { resource "google_project_iam_binding" "prod_spoke_project_iam_delegated" { project = module.prod-spoke-project.project_id role = "roles/resourcemanager.projectIamAdmin" - members = [ + members = compact([ local.service_accounts.data-platform-prod, local.service_accounts.project-factory-prod, - ] + ]) condition { title = "prod_stage3_sa_delegated_grants" description = "Production host project delegated grants." diff --git a/fast/stages/02-networking-peering/spoke-dev.tf b/fast/stages/02-networking-peering/spoke-dev.tf index a6713eaf..69c5b8eb 100644 --- a/fast/stages/02-networking-peering/spoke-dev.tf +++ b/fast/stages/02-networking-peering/spoke-dev.tf @@ -41,7 +41,7 @@ module "dev-spoke-project" { } metric_scopes = [module.landing-project.project_id] iam = { - "roles/dns.admin" = [local.service_accounts.project-factory-dev] + "roles/dns.admin" = compact([local.service_accounts.project-factory-dev]) } } @@ -100,10 +100,10 @@ module "dev-spoke-cloudnat" { resource "google_project_iam_binding" "dev_spoke_project_iam_delegated" { project = module.dev-spoke-project.project_id role = "roles/resourcemanager.projectIamAdmin" - members = [ + members = compact([ local.service_accounts.data-platform-dev, local.service_accounts.project-factory-dev, - ] + ]) condition { title = "dev_stage3_sa_delegated_grants" description = "Development host project delegated grants." diff --git a/fast/stages/02-networking-peering/spoke-prod.tf b/fast/stages/02-networking-peering/spoke-prod.tf index 401a0856..c8ded75b 100644 --- a/fast/stages/02-networking-peering/spoke-prod.tf +++ b/fast/stages/02-networking-peering/spoke-prod.tf @@ -41,7 +41,7 @@ module "prod-spoke-project" { } metric_scopes = [module.landing-project.project_id] iam = { - "roles/dns.admin" = [local.service_accounts.project-factory-prod] + "roles/dns.admin" = compact([local.service_accounts.project-factory-prod]) } } @@ -100,10 +100,10 @@ module "prod-spoke-cloudnat" { resource "google_project_iam_binding" "prod_spoke_project_iam_delegated" { project = module.prod-spoke-project.project_id role = "roles/resourcemanager.projectIamAdmin" - members = [ + members = compact([ local.service_accounts.data-platform-prod, local.service_accounts.project-factory-prod, - ] + ]) condition { title = "prod_stage3_sa_delegated_grants" description = "Production host project delegated grants." diff --git a/fast/stages/02-networking-vpn/spoke-dev.tf b/fast/stages/02-networking-vpn/spoke-dev.tf index a6713eaf..69c5b8eb 100644 --- a/fast/stages/02-networking-vpn/spoke-dev.tf +++ b/fast/stages/02-networking-vpn/spoke-dev.tf @@ -41,7 +41,7 @@ module "dev-spoke-project" { } metric_scopes = [module.landing-project.project_id] iam = { - "roles/dns.admin" = [local.service_accounts.project-factory-dev] + "roles/dns.admin" = compact([local.service_accounts.project-factory-dev]) } } @@ -100,10 +100,10 @@ module "dev-spoke-cloudnat" { resource "google_project_iam_binding" "dev_spoke_project_iam_delegated" { project = module.dev-spoke-project.project_id role = "roles/resourcemanager.projectIamAdmin" - members = [ + members = compact([ local.service_accounts.data-platform-dev, local.service_accounts.project-factory-dev, - ] + ]) condition { title = "dev_stage3_sa_delegated_grants" description = "Development host project delegated grants." diff --git a/fast/stages/02-networking-vpn/spoke-prod.tf b/fast/stages/02-networking-vpn/spoke-prod.tf index 401a0856..c8ded75b 100644 --- a/fast/stages/02-networking-vpn/spoke-prod.tf +++ b/fast/stages/02-networking-vpn/spoke-prod.tf @@ -41,7 +41,7 @@ module "prod-spoke-project" { } metric_scopes = [module.landing-project.project_id] iam = { - "roles/dns.admin" = [local.service_accounts.project-factory-prod] + "roles/dns.admin" = compact([local.service_accounts.project-factory-prod]) } } @@ -100,10 +100,10 @@ module "prod-spoke-cloudnat" { resource "google_project_iam_binding" "prod_spoke_project_iam_delegated" { project = module.prod-spoke-project.project_id role = "roles/resourcemanager.projectIamAdmin" - members = [ + members = compact([ local.service_accounts.data-platform-prod, local.service_accounts.project-factory-prod, - ] + ]) condition { title = "prod_stage3_sa_delegated_grants" description = "Production host project delegated grants." diff --git a/fast/stages/02-security/core-dev.tf b/fast/stages/02-security/core-dev.tf index b917a5cc..c6b9ba01 100644 --- a/fast/stages/02-security/core-dev.tf +++ b/fast/stages/02-security/core-dev.tf @@ -16,8 +16,10 @@ locals { dev_kms_restricted_admins = [ - "serviceAccount:${var.service_accounts.project-factory-dev}", - "serviceAccount:${var.service_accounts.data-platform-dev}" + for sa in compact([ + var.service_accounts.project-factory-dev, + var.service_accounts.data-platform-dev + ]) : "serviceAccount:${sa}" ] } diff --git a/fast/stages/02-security/core-prod.tf b/fast/stages/02-security/core-prod.tf index f792671f..b2154760 100644 --- a/fast/stages/02-security/core-prod.tf +++ b/fast/stages/02-security/core-prod.tf @@ -16,8 +16,10 @@ locals { prod_kms_restricted_admins = [ - "serviceAccount:${var.service_accounts.project-factory-prod}", - "serviceAccount:${var.service_accounts.data-platform-prod}" + for sa in compact([ + var.service_accounts.project-factory-prod, + var.service_accounts.data-platform-prod + ]) : "serviceAccount:${sa}" ] }