diff --git a/fast/assets/templates/workflow-gitlab.yaml b/fast/assets/templates/workflow-gitlab.yaml index 8981e70b..13057e11 100644 --- a/fast/assets/templates/workflow-gitlab.yaml +++ b/fast/assets/templates/workflow-gitlab.yaml @@ -1,4 +1,4 @@ -# Copyright 2022 Google LLC +# Copyright 2023 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -13,8 +13,6 @@ # limitations under the License. default: - before_script: - - echo "$${CI_JOB_JWT_V2}" > token.txt image: name: hashicorp/terraform entrypoint: @@ -27,7 +25,9 @@ variables: FAST_SERVICE_ACCOUNT: ${service_account} FAST_WIF_PROVIDER: ${identity_provider} SSH_AUTH_SOCK: /tmp/ssh_agent.sock + %{~ if tf_providers_file != "" ~} TF_PROVIDERS_FILE: ${tf_providers_file} + %{~ endif ~} TF_VAR_FILES: ${tf_var_files == [] ? "''" : join("\n ", tf_var_files)} stages: @@ -40,13 +40,26 @@ cache: key: gcp-auth paths: - cicd-sa-credentials.json - - .tf-setup + - token.txt + %{~ if tf_providers_file != "" ~} + - ${tf_providers_file} + %{~ endif ~} + %{~ for f in tf_var_files ~} + - ${f} + %{~ endfor ~} gcp-auth: + id_tokens: + GITLAB_TOKEN: + aud: + %{~ for aud in audiences ~} + - ${aud} + %{~ endfor ~} image: name: google/cloud-sdk:slim stage: gcp-auth script: + - echo "$${GITLAB_TOKEN}" > token.txt - | gcloud iam workload-identity-pools create-cred-config \ $${FAST_WIF_PROVIDER} \ @@ -54,6 +67,7 @@ gcp-auth: --service-account-token-lifetime-seconds=3600 \ --output-file=$${GOOGLE_CREDENTIALS} \ --credential-source-file=token.txt + tf-files: dependencies: - gcp-auth @@ -63,15 +77,18 @@ tf-files: script: # - gcloud components install -q alpha - gcloud config set auth/credential_file_override $${GOOGLE_CREDENTIALS} - - mkdir -p .tf-setup - - | - gcloud alpha storage cp -r \ - "gs://$${FAST_OUTPUTS_BUCKET}/providers/$${TF_PROVIDERS_FILE}" .tf-setup/ - - | - gcloud alpha storage cp -r \ - "gs://$${FAST_OUTPUTS_BUCKET}/tfvars" .tf-setup/ + %{~ if tf_providers_file != "" ~} + - gcloud alpha storage cp -r "gs://$${FAST_OUTPUTS_BUCKET}/providers/${tf_providers_file}" ./ + %{~ endif ~} + %{~ for f in tf_var_files ~} + - gcloud alpha storage cp -r "gs://$${FAST_OUTPUTS_BUCKET}/tfvars/${f}" ./ + %{~ endfor ~} + - ls -l tf-plan: + dependencies: + - tf-files + stage: tf-plan # uncomment the following lines and set the SSH key secret for private modules repo # before_script: # - | @@ -80,20 +97,15 @@ tf-plan: # mkdir -p ~/.ssh # ssh-keyscan -H 'gitlab.com' >> ~/.ssh/known_hosts # ssh-keyscan gitlab.com | sort -u - ~/.ssh/known_hosts -o ~/.ssh/known_hosts - stage: tf-plan script: - - cp .tf-setup/$${TF_PROVIDERS_FILE} ./ - - | - for f in $${TF_VAR_FILES}; do - ln -s ".tf-setup/tfvars/$f" ./ - done - terraform init - terraform validate - terraform plan - dependencies: - - tf-files tf-apply: + dependencies: + - tf-files + stage: tf-apply # uncomment the following lines and set the SSH key secret for private modules repo # before_script: # - | @@ -102,18 +114,10 @@ tf-apply: # mkdir -p ~/.ssh # ssh-keyscan -H 'gitlab.com' >> ~/.ssh/known_hosts # ssh-keyscan gitlab.com | sort -u - ~/.ssh/known_hosts -o ~/.ssh/known_hosts - stage: tf-apply script: - - cp .tf-setup/$${TF_PROVIDERS_FILE} ./ - - | - for f in $${TF_VAR_FILES}; do - ln -s ".tf-setup/tfvars/$f" ./ - done - terraform init - terraform validate - terraform apply -input=false -auto-approve - dependencies: - - tf-files when: manual only: variables: diff --git a/fast/stages-multitenant/0-bootstrap-tenant/README.md b/fast/stages-multitenant/0-bootstrap-tenant/README.md index a10b808d..f831f9af 100644 --- a/fast/stages-multitenant/0-bootstrap-tenant/README.md +++ b/fast/stages-multitenant/0-bootstrap-tenant/README.md @@ -175,7 +175,6 @@ This configuration is possible but unsupported and only exists for development p - ## Files | name | description | modules | resources | @@ -197,35 +196,34 @@ This configuration is possible but unsupported and only exists for development p | name | description | type | required | default | producer | |---|---|:---:|:---:|:---:|:---:| -| [automation](variables.tf#L20) | Automation resources created by the organization-level bootstrap stage. | object({…}) | ✓ | | 0-bootstrap | -| [billing_account](variables.tf#L38) | Billing account id. If billing account is not part of the same org set `is_org_level` to `false`. To disable handling of billing IAM roles set `no_iam` to `true`. | object({…}) | ✓ | | | -| [organization](variables.tf#L191) | Organization details. | object({…}) | ✓ | | 0-bootstrap | -| [prefix](variables.tf#L207) | Prefix used for resources that need unique names. Use 9 characters or less. | string | ✓ | | 0-bootstrap | -| [tag_keys](variables.tf#L230) | Organization tag keys. | object({…}) | ✓ | | 1-resman | -| [tag_names](variables.tf#L241) | Customized names for resource management tags. | object({…}) | ✓ | | 1-resman | -| [tag_values](variables.tf#L252) | Organization resource management tag values. | map(string) | ✓ | | 1-resman | -| [tenant_config](variables.tf#L259) | Tenant configuration. Short name must be 4 characters or less. If `short_name_is_prefix` is true, short name must be 9 characters or less, and will be used as the prefix as is. | object({…}) | ✓ | | | -| [cicd_repositories](variables.tf#L48) | 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_roles](variables.tf#L94) | Custom roles defined at the organization level, in key => id format. | object({…}) | | null | 0-bootstrap | -| [fast_features](variables.tf#L104) | Selective control for top-level FAST features. | object({…}) | | {} | 0-bootstrap | -| [federated_identity_providers](variables.tf#L118) | Workload Identity Federation pools. The `cicd_repositories` variable references keys here. | map(object({…})) | | {} | | -| [group_iam](variables.tf#L132) | Tenant-level custom group IAM settings in group => [roles] format. | map(list(string)) | | {} | | -| [iam](variables.tf#L138) | Tenant-level custom IAM settings in role => [principal] format. | map(list(string)) | | {} | | -| [iam_additive](variables.tf#L144) | Tenant-level custom IAM settings in role => [principal] format for non-authoritative bindings. | map(list(string)) | | {} | | -| [locations](variables.tf#L150) | Optional locations for GCS, BigQuery, and logging buckets created here. These are the defaults set at the organization level, and can be overridden via the tenant config variable. | object({…}) | | {…} | 0-bootstrap | -| [log_sinks](variables.tf#L170) | Tenant-level log sinks, in name => {type, filter} format. | map(object({…})) | | {…} | | -| [outputs_location](variables.tf#L201) | Enable writing provider, tfvars and CI/CD workflow files to local filesystem. Leave null to disable. | string | | null | | -| [project_parent_ids](variables.tf#L217) | Optional parents for projects created here in folders/nnnnnnn format. Null values will use the tenant folder as parent. | object({…}) | | {…} | | -| [test_principal](variables.tf#L300) | Used when testing to bypass the data source returning the current identity. | string | | null | | +| [automation](variables.tf#L20) | Automation resources created by the organization-level bootstrap stage. | object({…}) | ✓ | | 0-bootstrap | +| [billing_account](variables.tf#L39) | Billing account id. If billing account is not part of the same org set `is_org_level` to `false`. To disable handling of billing IAM roles set `no_iam` to `true`. | object({…}) | ✓ | | | +| [organization](variables.tf#L192) | Organization details. | object({…}) | ✓ | | 0-bootstrap | +| [prefix](variables.tf#L208) | Prefix used for resources that need unique names. Use 9 characters or less. | string | ✓ | | 0-bootstrap | +| [tag_keys](variables.tf#L231) | Organization tag keys. | object({…}) | ✓ | | 1-resman | +| [tag_names](variables.tf#L242) | Customized names for resource management tags. | object({…}) | ✓ | | 1-resman | +| [tag_values](variables.tf#L253) | Organization resource management tag values. | map(string) | ✓ | | 1-resman | +| [tenant_config](variables.tf#L260) | Tenant configuration. Short name must be 4 characters or less. If `short_name_is_prefix` is true, short name must be 9 characters or less, and will be used as the prefix as is. | object({…}) | ✓ | | | +| [cicd_repositories](variables.tf#L49) | 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_roles](variables.tf#L95) | Custom roles defined at the organization level, in key => id format. | object({…}) | | null | 0-bootstrap | +| [fast_features](variables.tf#L105) | Selective control for top-level FAST features. | object({…}) | | {} | 0-bootstrap | +| [federated_identity_providers](variables.tf#L119) | Workload Identity Federation pools. The `cicd_repositories` variable references keys here. | map(object({…})) | | {} | | +| [group_iam](variables.tf#L133) | Tenant-level custom group IAM settings in group => [roles] format. | map(list(string)) | | {} | | +| [iam](variables.tf#L139) | Tenant-level custom IAM settings in role => [principal] format. | map(list(string)) | | {} | | +| [iam_additive](variables.tf#L145) | Tenant-level custom IAM settings in role => [principal] format for non-authoritative bindings. | map(list(string)) | | {} | | +| [locations](variables.tf#L151) | Optional locations for GCS, BigQuery, and logging buckets created here. These are the defaults set at the organization level, and can be overridden via the tenant config variable. | object({…}) | | {…} | 0-bootstrap | +| [log_sinks](variables.tf#L171) | Tenant-level log sinks, in name => {type, filter} format. | map(object({…})) | | {…} | | +| [outputs_location](variables.tf#L202) | Enable writing provider, tfvars and CI/CD workflow files to local filesystem. Leave null to disable. | string | | null | | +| [project_parent_ids](variables.tf#L218) | Optional parents for projects created here in folders/nnnnnnn format. Null values will use the tenant folder as parent. | object({…}) | | {…} | | +| [test_principal](variables.tf#L301) | Used when testing to bypass the data source returning the current identity. | string | | null | | ## Outputs | name | description | sensitive | consumers | |---|---|:---:|---| -| [cicd_workflows](outputs.tf#L107) | CI/CD workflows for tenant bootstrap and resource management stages. | ✓ | | -| [federated_identity](outputs.tf#L113) | Workload Identity Federation pool and providers. | | | -| [provider](outputs.tf#L123) | Terraform provider file for tenant resource management stage. | ✓ | stage-01 | -| [tenant_resources](outputs.tf#L130) | Tenant-level resources. | | | -| [tfvars](outputs.tf#L141) | Terraform variable files for the following tenant stages. | ✓ | | - +| [cicd_workflows](outputs.tf#L109) | CI/CD workflows for tenant bootstrap and resource management stages. | ✓ | | +| [federated_identity](outputs.tf#L115) | Workload Identity Federation pool and providers. | | | +| [provider](outputs.tf#L125) | Terraform provider file for tenant resource management stage. | ✓ | stage-01 | +| [tenant_resources](outputs.tf#L132) | Tenant-level resources. | | | +| [tfvars](outputs.tf#L143) | Terraform variable files for the following tenant stages. | ✓ | | diff --git a/fast/stages-multitenant/0-bootstrap-tenant/cicd.tf b/fast/stages-multitenant/0-bootstrap-tenant/cicd.tf index a25215af..fd0ec167 100644 --- a/fast/stages-multitenant/0-bootstrap-tenant/cicd.tf +++ b/fast/stages-multitenant/0-bootstrap-tenant/cicd.tf @@ -20,23 +20,27 @@ locals { _file_prefix = "tenants/${var.tenant_config.short_name}" # derive identity pool names from identity providers for easy reference cicd_identity_pools = { - for k, v in local.cicd_identity_providers : + for k, v in local.cicd_providers : k => split("/providers/", v.name)[0] } # merge org-level and tenant-level identity providers - cicd_identity_providers = merge( + cicd_providers = merge( var.automation.federated_identity_providers, { for k, v in google_iam_workload_identity_pool_provider.default : k => { + audiences = concat( + v.oidc[0].allowed_audiences, + ["https://iam.googleapis.com/${v.name}"] + ) issuer = local.identity_providers[k].issuer - issuer_uri = local.identity_providers[k].issuer_uri + issuer_uri = try(v.oidc[0].issuer_uri, null) name = v.name principal_tpl = local.identity_providers[k].principal_tpl principalset_tpl = local.identity_providers[k].principalset_tpl } - }) - # filter CI/CD repositories to only keep valid ones + } + ) cicd_repositories = { for k, v in coalesce(var.cicd_repositories, {}) : k => v if( @@ -46,7 +50,7 @@ locals { try(v.type, null) == "sourcerepo" || contains( - keys(local.cicd_identity_providers), + keys(local.cicd_providers), coalesce(try(v.identity_provider, null), ":") ) ) @@ -111,12 +115,12 @@ module "automation-tf-cicd-sa-bootstrap" { "roles/iam.workloadIdentityUser" = [ each.value.branch == null ? format( - local.cicd_identity_providers[each.value.identity_provider].principalset_tpl, + local.cicd_providers[each.value.identity_provider].principalset_tpl, local.cicd_identity_pools[each.value.identity_provider], each.value.name ) : format( - local.cicd_identity_providers[each.value.identity_provider].principal_tpl, + local.cicd_providers[each.value.identity_provider].principal_tpl, local.cicd_identity_pools[each.value.identity_provider], each.value.name, each.value.branch @@ -201,12 +205,12 @@ module "automation-tf-cicd-sa-resman" { "roles/iam.workloadIdentityUser" = [ each.value.branch == null ? format( - local.cicd_identity_providers[each.value.identity_provider].principalset_tpl, + local.cicd_providers[each.value.identity_provider].principalset_tpl, local.cicd_identity_pools[each.value.identity_provider], each.value.name ) : format( - local.cicd_identity_providers[each.value.identity_provider].principal_tpl, + local.cicd_providers[each.value.identity_provider].principal_tpl, local.cicd_identity_pools[each.value.identity_provider], each.value.name, each.value.branch diff --git a/fast/stages-multitenant/0-bootstrap-tenant/identity-providers.tf b/fast/stages-multitenant/0-bootstrap-tenant/identity-providers.tf index 3f8499b7..085a30f0 100644 --- a/fast/stages-multitenant/0-bootstrap-tenant/identity-providers.tf +++ b/fast/stages-multitenant/0-bootstrap-tenant/identity-providers.tf @@ -38,7 +38,7 @@ locals { principal_tpl = "principal://iam.googleapis.com/%s/subject/repo:%s:ref:refs/heads/%s" principalset_tpl = "principalSet://iam.googleapis.com/%s/attribute.repository/%s" } - # https://docs.gitlab.com/ee/ci/cloud_services/index.html#how-it-works + # https://docs.gitlab.com/ee/ci/secrets/id_token_authentication.html#token-payload gitlab = { attribute_mapping = { "google.subject" = "assertion.sub" @@ -56,10 +56,9 @@ locals { "attribute.ref_protected" = "assertion.ref_protected" "attribute.ref_type" = "assertion.ref_type" } - allowed_audiences = ["https://gitlab.com"] - issuer_uri = "https://gitlab.com" - principal_tpl = "principalSet://iam.googleapis.com/%s/attribute.sub/project_path:%s:ref_type:branch:ref:%s" - principalset_tpl = "principalSet://iam.googleapis.com/%s/attribute.repository/%s" + issuer_uri = "https://gitlab.com" + principal_tpl = "principalSet://iam.googleapis.com/%s/attribute.sub/project_path:%s:ref_type:branch:ref:%s" + principalset_tpl = "principalSet://iam.googleapis.com/%s/attribute.repository/%s" } } } @@ -82,13 +81,11 @@ resource "google_iam_workload_identity_pool_provider" "default" { attribute_condition = each.value.attribute_condition attribute_mapping = each.value.attribute_mapping oidc { - allowed_audiences = ( - try(each.value.custom_settings.allowed_audiences, null) != null - ? each.value.custom_settings.allowed_audiences - : try(each.value.allowed_audiences, null) - ) + # Setting an empty list configures allowed_audiences to the url of the provider + allowed_audiences = each.value.custom_settings.audiences + # If users don't provide an issuer_uri, we set the public one for the plaform choosed. issuer_uri = ( - try(each.value.custom_settings.issuer_uri, null) != null + each.value.custom_settings.issuer_uri != null ? each.value.custom_settings.issuer_uri : try(each.value.issuer_uri, null) ) diff --git a/fast/stages-multitenant/0-bootstrap-tenant/outputs.tf b/fast/stages-multitenant/0-bootstrap-tenant/outputs.tf index c61c584e..883acac9 100644 --- a/fast/stages-multitenant/0-bootstrap-tenant/outputs.tf +++ b/fast/stages-multitenant/0-bootstrap-tenant/outputs.tf @@ -20,8 +20,9 @@ locals { "${path.module}/templates/workflow-${v.type}.yaml", ( k == "bootstrap" ? { + audiences = local.cicd_providers[v["identity_provider"]].audiences identity_provider = try( - local.cicd_identity_providers[v["identity_provider"]].name, "" + local.cicd_providers[v["identity_provider"]].name, "" ) outputs_bucket = var.automation.outputs_bucket service_account = try( @@ -36,8 +37,9 @@ locals { ] } : { + audiences = local.cicd_providers[v["identity_provider"]].audiences identity_provider = try( - local.cicd_identity_providers[v["identity_provider"]].name, "" + local.cicd_providers[v["identity_provider"]].name, "" ) outputs_bucket = module.automation-tf-output-gcs.name service_account = try( @@ -70,7 +72,7 @@ locals { try(google_iam_workload_identity_pool.default.0.name, null), var.automation.federated_identity_pool, ]) - federated_identity_providers = local.cicd_identity_providers + federated_identity_providers = local.cicd_providers service_accounts = merge( { resman = module.automation-tf-resman-sa.email }, { @@ -116,7 +118,7 @@ output "federated_identity" { pool = try( google_iam_workload_identity_pool.default.0.name, null ) - providers = local.cicd_identity_providers + providers = local.cicd_providers } } diff --git a/fast/stages-multitenant/0-bootstrap-tenant/templates/workflow-gitlab.yaml b/fast/stages-multitenant/0-bootstrap-tenant/templates/workflow-gitlab.yaml index 739e7485..13057e11 100644 --- a/fast/stages-multitenant/0-bootstrap-tenant/templates/workflow-gitlab.yaml +++ b/fast/stages-multitenant/0-bootstrap-tenant/templates/workflow-gitlab.yaml @@ -1,4 +1,4 @@ -# Copyright 2022 Google LLC +# Copyright 2023 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -13,8 +13,6 @@ # limitations under the License. default: - before_script: - - echo "$${CI_JOB_JWT_V2}" > token.txt image: name: hashicorp/terraform entrypoint: @@ -42,13 +40,26 @@ cache: key: gcp-auth paths: - cicd-sa-credentials.json - - .tf-setup + - token.txt + %{~ if tf_providers_file != "" ~} + - ${tf_providers_file} + %{~ endif ~} + %{~ for f in tf_var_files ~} + - ${f} + %{~ endfor ~} gcp-auth: + id_tokens: + GITLAB_TOKEN: + aud: + %{~ for aud in audiences ~} + - ${aud} + %{~ endfor ~} image: name: google/cloud-sdk:slim stage: gcp-auth script: + - echo "$${GITLAB_TOKEN}" > token.txt - | gcloud iam workload-identity-pools create-cred-config \ $${FAST_WIF_PROVIDER} \ @@ -56,6 +67,7 @@ gcp-auth: --service-account-token-lifetime-seconds=3600 \ --output-file=$${GOOGLE_CREDENTIALS} \ --credential-source-file=token.txt + tf-files: dependencies: - gcp-auth @@ -65,17 +77,18 @@ tf-files: script: # - gcloud components install -q alpha - gcloud config set auth/credential_file_override $${GOOGLE_CREDENTIALS} - - mkdir -p .tf-setup %{~ if tf_providers_file != "" ~} - - | - gcloud alpha storage cp -r \ - "gs://$${FAST_OUTPUTS_BUCKET}/providers/$${TF_PROVIDERS_FILE}" .tf-setup/ + - gcloud alpha storage cp -r "gs://$${FAST_OUTPUTS_BUCKET}/providers/${tf_providers_file}" ./ %{~ endif ~} - - | - gcloud alpha storage cp -r \ - "gs://$${FAST_OUTPUTS_BUCKET}/tfvars" .tf-setup/ + %{~ for f in tf_var_files ~} + - gcloud alpha storage cp -r "gs://$${FAST_OUTPUTS_BUCKET}/tfvars/${f}" ./ + %{~ endfor ~} + - ls -l tf-plan: + dependencies: + - tf-files + stage: tf-plan # uncomment the following lines and set the SSH key secret for private modules repo # before_script: # - | @@ -84,20 +97,15 @@ tf-plan: # mkdir -p ~/.ssh # ssh-keyscan -H 'gitlab.com' >> ~/.ssh/known_hosts # ssh-keyscan gitlab.com | sort -u - ~/.ssh/known_hosts -o ~/.ssh/known_hosts - stage: tf-plan script: - - cp .tf-setup/$${TF_PROVIDERS_FILE} ./ - - | - for f in $${TF_VAR_FILES}; do - ln -s ".tf-setup/tfvars/$f" ./ - done - terraform init - terraform validate - terraform plan - dependencies: - - tf-files tf-apply: + dependencies: + - tf-files + stage: tf-apply # uncomment the following lines and set the SSH key secret for private modules repo # before_script: # - | @@ -106,18 +114,10 @@ tf-apply: # mkdir -p ~/.ssh # ssh-keyscan -H 'gitlab.com' >> ~/.ssh/known_hosts # ssh-keyscan gitlab.com | sort -u - ~/.ssh/known_hosts -o ~/.ssh/known_hosts - stage: tf-apply script: - - cp .tf-setup/$${TF_PROVIDERS_FILE} ./ - - | - for f in $${TF_VAR_FILES}; do - ln -s ".tf-setup/tfvars/$f" ./ - done - terraform init - terraform validate - terraform apply -input=false -auto-approve - dependencies: - - tf-files when: manual only: variables: diff --git a/fast/stages-multitenant/0-bootstrap-tenant/variables.tf b/fast/stages-multitenant/0-bootstrap-tenant/variables.tf index 718818ea..af56db39 100644 --- a/fast/stages-multitenant/0-bootstrap-tenant/variables.tf +++ b/fast/stages-multitenant/0-bootstrap-tenant/variables.tf @@ -26,6 +26,7 @@ variable "automation" { project_number = string federated_identity_pool = string federated_identity_providers = map(object({ + audiences = list(string) issuer = string issuer_uri = string name = string @@ -118,12 +119,12 @@ variable "fast_features" { variable "federated_identity_providers" { description = "Workload Identity Federation pools. The `cicd_repositories` variable references keys here." type = map(object({ - attribute_condition = string + attribute_condition = optional(string) issuer = string - custom_settings = object({ - issuer_uri = string - allowed_audiences = list(string) - }) + custom_settings = optional(object({ + issuer_uri = optional(string) + audiences = optional(list(string), []) + }), {}) })) default = {} nullable = false diff --git a/fast/stages-multitenant/1-resman-tenant/README.md b/fast/stages-multitenant/1-resman-tenant/README.md index 9e1188bb..c38f173f 100644 --- a/fast/stages-multitenant/1-resman-tenant/README.md +++ b/fast/stages-multitenant/1-resman-tenant/README.md @@ -126,7 +126,6 @@ Once the configuration is done just go through the usual `init/apply` cycle. On - ## Files | name | description | modules | resources | @@ -154,21 +153,21 @@ Once the configuration is done just go through the usual `init/apply` cycle. On | name | description | type | required | default | producer | |---|---|:---:|:---:|:---:|:---:| -| [automation](variables.tf#L20) | Automation resources created by the bootstrap stage. | object({…}) | ✓ | | 0-bootstrap | -| [billing_account](variables.tf#L51) | Billing account id. If billing account is not part of the same org set `is_org_level` to `false`. To disable handling of billing IAM roles set `no_iam` to `true`. | object({…}) | ✓ | | 0-bootstrap | -| [organization](variables.tf#L204) | Organization details. | object({…}) | ✓ | | 0-bootstrap | -| [prefix](variables.tf#L226) | Prefix used for resources that need unique names. Use 9 characters or less. | string | ✓ | | 0-bootstrap | +| [automation](variables.tf#L20) | Automation resources created by the bootstrap stage. | object({…}) | ✓ | | 0-bootstrap | +| [billing_account](variables.tf#L52) | Billing account id. If billing account is not part of the same org set `is_org_level` to `false`. To disable handling of billing IAM roles set `no_iam` to `true`. | object({…}) | ✓ | | 0-bootstrap | +| [organization](variables.tf#L205) | Organization details. | object({…}) | ✓ | | 0-bootstrap | +| [prefix](variables.tf#L227) | Prefix used for resources that need unique names. Use 9 characters or less. | string | ✓ | | 0-bootstrap | | [root_node](variables.tf#L237) | Root folder node for the tenant, in folders/nnnnnn format. | string | ✓ | | | | [short_name](variables.tf#L242) | Short name used to identify the tenant. | string | ✓ | | | | [tags](variables.tf#L247) | Resource management tags. | object({…}) | ✓ | | | -| [cicd_repositories](variables.tf#L62) | 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#L144) | Custom roles defined at the org level, in key => id format. | object({…}) | | null | 0-bootstrap | -| [data_dir](variables.tf#L153) | Relative path for the folder storing configuration data. | string | | "data" | | -| [fast_features](variables.tf#L159) | Selective control for top-level FAST features. | object({…}) | | {} | 0-0-bootstrap | -| [groups](variables.tf#L173) | Group names to grant organization-level permissions. | object({…}) | | {} | 0-bootstrap | -| [locations](variables.tf#L186) | Optional locations for GCS, BigQuery, and logging buckets created here. | object({…}) | | {…} | 0-bootstrap | -| [organization_policy_data_path](variables.tf#L214) | Path for the data folder used by the organization policies factory. | string | | null | | -| [outputs_location](variables.tf#L220) | Enable writing provider, tfvars and CI/CD workflow files to local filesystem. Leave null to disable. | string | | null | | +| [cicd_repositories](variables.tf#L63) | 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#L145) | Custom roles defined at the org level, in key => id format. | object({…}) | | null | 0-bootstrap | +| [data_dir](variables.tf#L154) | Relative path for the folder storing configuration data. | string | | "data" | | +| [fast_features](variables.tf#L160) | Selective control for top-level FAST features. | object({…}) | | {} | 0-0-bootstrap | +| [groups](variables.tf#L174) | Group names to grant organization-level permissions. | object({…}) | | {} | 0-bootstrap | +| [locations](variables.tf#L187) | Optional locations for GCS, BigQuery, and logging buckets created here. | object({…}) | | {…} | 0-bootstrap | +| [organization_policy_data_path](variables.tf#L215) | Path for the data folder used by the organization policies factory. | string | | null | | +| [outputs_location](variables.tf#L221) | Enable writing provider, tfvars and CI/CD workflow files to local filesystem. Leave null to disable. | string | | null | | | [team_folders](variables.tf#L265) | Team folders to be created. Format is described in a code comment. | map(object({…})) | | null | | | [test_skip_data_sources](variables.tf#L275) | Used when testing to bypass data sources. | bool | | false | | @@ -176,15 +175,14 @@ Once the configuration is done just go through the usual `init/apply` cycle. On | name | description | sensitive | consumers | |---|---|:---:|---| -| [cicd_repositories](outputs.tf#L189) | WIF configuration for CI/CD repositories. | | | -| [dataplatform](outputs.tf#L203) | Data for the Data Platform stage. | | | -| [gke_multitenant](outputs.tf#L219) | Data for the GKE multitenant stage. | | 03-gke-multitenant | -| [networking](outputs.tf#L240) | Data for the networking stage. | | | -| [project_factories](outputs.tf#L249) | Data for the project factories stage. | | | -| [providers](outputs.tf#L264) | Terraform provider files for this stage and dependent stages. | ✓ | 02-networking · 02-security · 03-dataplatform · xx-sandbox · xx-teams | -| [sandbox](outputs.tf#L271) | Data for the sandbox stage. | | xx-sandbox | -| [security](outputs.tf#L285) | Data for the networking stage. | | 02-security | -| [teams](outputs.tf#L295) | Data for the teams stage. | | | -| [tfvars](outputs.tf#L307) | Terraform variable files for the following stages. | ✓ | | - +| [cicd_repositories](outputs.tf#L192) | WIF configuration for CI/CD repositories. | | | +| [dataplatform](outputs.tf#L206) | Data for the Data Platform stage. | | | +| [gke_multitenant](outputs.tf#L222) | Data for the GKE multitenant stage. | | 03-gke-multitenant | +| [networking](outputs.tf#L243) | Data for the networking stage. | | | +| [project_factories](outputs.tf#L252) | Data for the project factories stage. | | | +| [providers](outputs.tf#L267) | Terraform provider files for this stage and dependent stages. | ✓ | 02-networking · 02-security · 03-dataplatform · xx-sandbox · xx-teams | +| [sandbox](outputs.tf#L274) | Data for the sandbox stage. | | xx-sandbox | +| [security](outputs.tf#L288) | Data for the networking stage. | | 02-security | +| [teams](outputs.tf#L298) | Data for the teams stage. | | | +| [tfvars](outputs.tf#L310) | Terraform variable files for the following stages. | ✓ | | diff --git a/fast/stages-multitenant/1-resman-tenant/outputs.tf b/fast/stages-multitenant/1-resman-tenant/outputs.tf index 592f995e..dadfb057 100644 --- a/fast/stages-multitenant/1-resman-tenant/outputs.tf +++ b/fast/stages-multitenant/1-resman-tenant/outputs.tf @@ -62,6 +62,9 @@ locals { for k, v in local.cicd_repositories : k => templatefile( "${path.module}/templates/workflow-${v.type}.yaml", merge(local.cicd_workflow_attrs[k], { + audiences = try( + local.cicd_identity_providers[v.identity_provider].audiences, null + ) identity_provider = try( local.cicd_identity_providers[v.identity_provider].name, null ) diff --git a/fast/stages-multitenant/1-resman-tenant/templates/workflow-gitlab.yaml b/fast/stages-multitenant/1-resman-tenant/templates/workflow-gitlab.yaml index 8981e70b..13057e11 100644 --- a/fast/stages-multitenant/1-resman-tenant/templates/workflow-gitlab.yaml +++ b/fast/stages-multitenant/1-resman-tenant/templates/workflow-gitlab.yaml @@ -1,4 +1,4 @@ -# Copyright 2022 Google LLC +# Copyright 2023 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -13,8 +13,6 @@ # limitations under the License. default: - before_script: - - echo "$${CI_JOB_JWT_V2}" > token.txt image: name: hashicorp/terraform entrypoint: @@ -27,7 +25,9 @@ variables: FAST_SERVICE_ACCOUNT: ${service_account} FAST_WIF_PROVIDER: ${identity_provider} SSH_AUTH_SOCK: /tmp/ssh_agent.sock + %{~ if tf_providers_file != "" ~} TF_PROVIDERS_FILE: ${tf_providers_file} + %{~ endif ~} TF_VAR_FILES: ${tf_var_files == [] ? "''" : join("\n ", tf_var_files)} stages: @@ -40,13 +40,26 @@ cache: key: gcp-auth paths: - cicd-sa-credentials.json - - .tf-setup + - token.txt + %{~ if tf_providers_file != "" ~} + - ${tf_providers_file} + %{~ endif ~} + %{~ for f in tf_var_files ~} + - ${f} + %{~ endfor ~} gcp-auth: + id_tokens: + GITLAB_TOKEN: + aud: + %{~ for aud in audiences ~} + - ${aud} + %{~ endfor ~} image: name: google/cloud-sdk:slim stage: gcp-auth script: + - echo "$${GITLAB_TOKEN}" > token.txt - | gcloud iam workload-identity-pools create-cred-config \ $${FAST_WIF_PROVIDER} \ @@ -54,6 +67,7 @@ gcp-auth: --service-account-token-lifetime-seconds=3600 \ --output-file=$${GOOGLE_CREDENTIALS} \ --credential-source-file=token.txt + tf-files: dependencies: - gcp-auth @@ -63,15 +77,18 @@ tf-files: script: # - gcloud components install -q alpha - gcloud config set auth/credential_file_override $${GOOGLE_CREDENTIALS} - - mkdir -p .tf-setup - - | - gcloud alpha storage cp -r \ - "gs://$${FAST_OUTPUTS_BUCKET}/providers/$${TF_PROVIDERS_FILE}" .tf-setup/ - - | - gcloud alpha storage cp -r \ - "gs://$${FAST_OUTPUTS_BUCKET}/tfvars" .tf-setup/ + %{~ if tf_providers_file != "" ~} + - gcloud alpha storage cp -r "gs://$${FAST_OUTPUTS_BUCKET}/providers/${tf_providers_file}" ./ + %{~ endif ~} + %{~ for f in tf_var_files ~} + - gcloud alpha storage cp -r "gs://$${FAST_OUTPUTS_BUCKET}/tfvars/${f}" ./ + %{~ endfor ~} + - ls -l tf-plan: + dependencies: + - tf-files + stage: tf-plan # uncomment the following lines and set the SSH key secret for private modules repo # before_script: # - | @@ -80,20 +97,15 @@ tf-plan: # mkdir -p ~/.ssh # ssh-keyscan -H 'gitlab.com' >> ~/.ssh/known_hosts # ssh-keyscan gitlab.com | sort -u - ~/.ssh/known_hosts -o ~/.ssh/known_hosts - stage: tf-plan script: - - cp .tf-setup/$${TF_PROVIDERS_FILE} ./ - - | - for f in $${TF_VAR_FILES}; do - ln -s ".tf-setup/tfvars/$f" ./ - done - terraform init - terraform validate - terraform plan - dependencies: - - tf-files tf-apply: + dependencies: + - tf-files + stage: tf-apply # uncomment the following lines and set the SSH key secret for private modules repo # before_script: # - | @@ -102,18 +114,10 @@ tf-apply: # mkdir -p ~/.ssh # ssh-keyscan -H 'gitlab.com' >> ~/.ssh/known_hosts # ssh-keyscan gitlab.com | sort -u - ~/.ssh/known_hosts -o ~/.ssh/known_hosts - stage: tf-apply script: - - cp .tf-setup/$${TF_PROVIDERS_FILE} ./ - - | - for f in $${TF_VAR_FILES}; do - ln -s ".tf-setup/tfvars/$f" ./ - done - terraform init - terraform validate - terraform apply -input=false -auto-approve - dependencies: - - tf-files when: manual only: variables: diff --git a/fast/stages-multitenant/1-resman-tenant/variables.tf b/fast/stages-multitenant/1-resman-tenant/variables.tf index 1c399be2..bcda7fd6 100644 --- a/fast/stages-multitenant/1-resman-tenant/variables.tf +++ b/fast/stages-multitenant/1-resman-tenant/variables.tf @@ -26,6 +26,7 @@ variable "automation" { project_number = string federated_identity_pools = list(string) federated_identity_providers = map(object({ + audiences = list(string) issuer = string issuer_uri = string name = string @@ -227,7 +228,6 @@ variable "prefix" { # tfdoc:variable:source 0-bootstrap description = "Prefix used for resources that need unique names. Use 9 characters or less." type = string - validation { condition = try(length(var.prefix), 0) < 13 error_message = "Use a maximum of 12 characters for prefix (which is a combination of org prefix and tenant short name)." diff --git a/fast/stages/0-bootstrap/README.md b/fast/stages/0-bootstrap/README.md index 105b044f..a9a28b1b 100644 --- a/fast/stages/0-bootstrap/README.md +++ b/fast/stages/0-bootstrap/README.md @@ -209,10 +209,10 @@ Then make sure you have configured the correct values for the following variable - `organization.id`, `organization.domain`, `organization.customer_id` the id, domain and customer id of your organization, derived from the Cloud Console UI or by running `gcloud organizations list` - `prefix` - the fixed org-level prefix used in your naming, maximum 9 characters long. Note that if you are using multitenant stages, then you will later need to configure a `tenant prefix`. - This `tenant prefix` can have a maximum length of 2 characters, - plus any unused characters from the from the `prefix`. - For example, if you specify a `prefix` that is 7 characters long, + the fixed org-level prefix used in your naming, maximum 9 characters long. Note that if you are using multitenant stages, then you will later need to configure a `tenant prefix`. + This `tenant prefix` can have a maximum length of 2 characters, + plus any unused characters from the from the `prefix`. + For example, if you specify a `prefix` that is 7 characters long, then your `tenant prefix` can have a maximum of 4 characters. You can also adapt the example that follows to your needs: @@ -421,7 +421,7 @@ federated_identity_providers = { attribute_condition = "attribute.namespace_path==\"my-gitlab-org\"" issuer = "gitlab" custom_settings = { - allowed_audiences = ["https://gitlab.fast.example.com"] + audiences = ["https://gitlab.fast.example.com"] issuer_uri = "https://gitlab.fast.example.com" } } @@ -516,7 +516,7 @@ The remaining configuration is manual, as it regards the repositories themselves | [custom_role_names](variables.tf#L79) | Names of custom roles defined at the org level. | object({…}) | | {…} | | | [custom_roles](variables.tf#L93) | Map of role names => list of permissions to additionally create at the organization level. | map(list(string)) | | {} | | | [fast_features](variables.tf#L100) | Selective control for top-level FAST features. | object({…}) | | {} | | -| [federated_identity_providers](variables.tf#L113) | Workload Identity Federation pools. The `cicd_repositories` variable references keys here. | map(object({…})) | | {} | | +| [federated_identity_providers](variables.tf#L113) | Workload Identity Federation pools. The `cicd_repositories` variable references keys here. | map(object({…})) | | {} | | | [groups](variables.tf#L132) | Group names or emails to grant organization-level permissions. If just the name is provided, the default organization domain is assumed. | map(string) | | {…} | | | [iam](variables.tf#L150) | Organization-level custom IAM settings in role => [principal] format. | map(list(string)) | | {} | | | [iam_additive](variables.tf#L156) | Organization-level custom IAM settings in role => [principal] format for non-authoritative bindings. | map(list(string)) | | {} | | diff --git a/fast/stages/0-bootstrap/cicd.tf b/fast/stages/0-bootstrap/cicd.tf index f0b321d9..96839ec3 100644 --- a/fast/stages/0-bootstrap/cicd.tf +++ b/fast/stages/0-bootstrap/cicd.tf @@ -20,9 +20,9 @@ locals { cicd_providers = { for k, v in google_iam_workload_identity_pool_provider.default : k => { - audience = try( - v.oidc[0].allowed_audiences[0], - "https://iam.googleapis.com/${v.name}" + audiences = concat( + v.oidc[0].allowed_audiences, + ["https://iam.googleapis.com/${v.name}"] ) issuer = local.identity_providers[k].issuer issuer_uri = try(v.oidc[0].issuer_uri, null) @@ -39,10 +39,15 @@ locals { ( try(v.type, null) == "sourcerepo" || - contains(keys(local.identity_providers), coalesce(try(v.identity_provider, null), ":")) + contains( + keys(local.identity_providers), + coalesce(try(v.identity_provider, null), ":") + ) ) && - fileexists(format("${path.module}/templates/workflow-%s.yaml", try(v.type, ""))) + fileexists( + format("${path.module}/templates/workflow-%s.yaml", try(v.type, "")) + ) ) } cicd_workflow_providers = { diff --git a/fast/stages/0-bootstrap/identity-providers.tf b/fast/stages/0-bootstrap/identity-providers.tf index 1c1c8b20..085a30f0 100644 --- a/fast/stages/0-bootstrap/identity-providers.tf +++ b/fast/stages/0-bootstrap/identity-providers.tf @@ -82,7 +82,7 @@ resource "google_iam_workload_identity_pool_provider" "default" { attribute_mapping = each.value.attribute_mapping oidc { # Setting an empty list configures allowed_audiences to the url of the provider - allowed_audiences = each.value.custom_settings.allowed_audiences + allowed_audiences = each.value.custom_settings.audiences # If users don't provide an issuer_uri, we set the public one for the plaform choosed. issuer_uri = ( each.value.custom_settings.issuer_uri != null diff --git a/fast/stages/0-bootstrap/outputs.tf b/fast/stages/0-bootstrap/outputs.tf index 1db70bc4..ea980fca 100644 --- a/fast/stages/0-bootstrap/outputs.tf +++ b/fast/stages/0-bootstrap/outputs.tf @@ -22,7 +22,7 @@ locals { "${path.module}/templates/workflow-${v.type}.yaml", { # If users give a list of custom audiences we set by default the first element. # If no audiences are given, we set https://iam.googleapis.com/{PROVIDER_NAME} - audience = local.cicd_providers[v["identity_provider"]].audience + audiences = local.cicd_providers[v["identity_provider"]].audiences identity_provider = try( local.cicd_providers[v["identity_provider"]].name, "" ) diff --git a/fast/stages/0-bootstrap/templates/workflow-gitlab.yaml b/fast/stages/0-bootstrap/templates/workflow-gitlab.yaml index 33e55d21..13057e11 100644 --- a/fast/stages/0-bootstrap/templates/workflow-gitlab.yaml +++ b/fast/stages/0-bootstrap/templates/workflow-gitlab.yaml @@ -20,13 +20,14 @@ default: - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" variables: - AUDIENCE: ${audience} GOOGLE_CREDENTIALS: cicd-sa-credentials.json FAST_OUTPUTS_BUCKET: ${outputs_bucket} FAST_SERVICE_ACCOUNT: ${service_account} FAST_WIF_PROVIDER: ${identity_provider} SSH_AUTH_SOCK: /tmp/ssh_agent.sock + %{~ if tf_providers_file != "" ~} TF_PROVIDERS_FILE: ${tf_providers_file} + %{~ endif ~} TF_VAR_FILES: ${tf_var_files == [] ? "''" : join("\n ", tf_var_files)} stages: @@ -38,16 +39,27 @@ stages: cache: key: gcp-auth paths: - - .tf-setup + - cicd-sa-credentials.json + - token.txt + %{~ if tf_providers_file != "" ~} + - ${tf_providers_file} + %{~ endif ~} + %{~ for f in tf_var_files ~} + - ${f} + %{~ endfor ~} gcp-auth: - stage: gcp-auth id_tokens: GITLAB_TOKEN: - aud: "$${AUDIENCE}" + aud: + %{~ for aud in audiences ~} + - ${aud} + %{~ endfor ~} image: name: google/cloud-sdk:slim + stage: gcp-auth script: + - echo "$${GITLAB_TOKEN}" > token.txt - | gcloud iam workload-identity-pools create-cred-config \ $${FAST_WIF_PROVIDER} \ @@ -55,30 +67,27 @@ gcp-auth: --service-account-token-lifetime-seconds=3600 \ --output-file=$${GOOGLE_CREDENTIALS} \ --credential-source-file=token.txt - - rm token.txt - artifacts: - untracked: true tf-files: - stage: tf-files + dependencies: + - gcp-auth image: name: google/cloud-sdk:slim + stage: tf-files script: # - gcloud components install -q alpha - gcloud config set auth/credential_file_override $${GOOGLE_CREDENTIALS} - - mkdir -p .tf-setup - - | - gcloud alpha storage cp -r \ - "gs://$${FAST_OUTPUTS_BUCKET}/providers/$${TF_PROVIDERS_FILE}" .tf-setup/ - - | - gcloud alpha storage cp -r \ - "gs://$${FAST_OUTPUTS_BUCKET}/tfvars" .tf-setup/ - artifacts: - untracked: true - dependencies: - - gcp-auth + %{~ if tf_providers_file != "" ~} + - gcloud alpha storage cp -r "gs://$${FAST_OUTPUTS_BUCKET}/providers/${tf_providers_file}" ./ + %{~ endif ~} + %{~ for f in tf_var_files ~} + - gcloud alpha storage cp -r "gs://$${FAST_OUTPUTS_BUCKET}/tfvars/${f}" ./ + %{~ endfor ~} + - ls -l tf-plan: + dependencies: + - tf-files stage: tf-plan # uncomment the following lines and set the SSH key secret for private modules repo # before_script: @@ -89,20 +98,13 @@ tf-plan: # ssh-keyscan -H 'gitlab.com' >> ~/.ssh/known_hosts # ssh-keyscan gitlab.com | sort -u - ~/.ssh/known_hosts -o ~/.ssh/known_hosts script: - - cp ".tf-setup/$${TF_PROVIDERS_FILE}" ./ - - | - for f in "$${TF_VAR_FILES}"; do - ln -s ".tf-setup/tfvars/$f" ./ - done - terraform init - terraform validate - terraform plan - artifacts: - untracked: true - dependencies: - - tf-files tf-apply: + dependencies: + - tf-files stage: tf-apply # uncomment the following lines and set the SSH key secret for private modules repo # before_script: @@ -113,18 +115,9 @@ tf-apply: # ssh-keyscan -H 'gitlab.com' >> ~/.ssh/known_hosts # ssh-keyscan gitlab.com | sort -u - ~/.ssh/known_hosts -o ~/.ssh/known_hosts script: - - cp .tf-setup/$${TF_PROVIDERS_FILE} ./ - - | - for f in $${TF_VAR_FILES}; do - ln -s ".tf-setup/tfvars/$f" ./ - done - terraform init - terraform validate - terraform apply -input=false -auto-approve - artifacts: - untracked: true - dependencies: - - tf-files when: manual only: variables: diff --git a/fast/stages/0-bootstrap/variables.tf b/fast/stages/0-bootstrap/variables.tf index f41d6dfa..1008c240 100644 --- a/fast/stages/0-bootstrap/variables.tf +++ b/fast/stages/0-bootstrap/variables.tf @@ -116,8 +116,8 @@ variable "federated_identity_providers" { attribute_condition = optional(string) issuer = string custom_settings = optional(object({ - issuer_uri = optional(string) - allowed_audiences = optional(list(string), []) + issuer_uri = optional(string) + audiences = optional(list(string), []) }), {}) })) default = {} diff --git a/fast/stages/1-resman/README.md b/fast/stages/1-resman/README.md index 4dec2945..d938020f 100644 --- a/fast/stages/1-resman/README.md +++ b/fast/stages/1-resman/README.md @@ -365,7 +365,7 @@ Due to its simplicity, this stage lends itself easily to customizations: adding | name | description | type | required | default | producer | |---|---|:---:|:---:|:---:|:---:| -| [automation](variables.tf#L20) | Automation resources created by the bootstrap stage. | object({…}) | ✓ | | 0-bootstrap | +| [automation](variables.tf#L20) | Automation resources created by the bootstrap stage. | object({…}) | ✓ | | 0-bootstrap | | [billing_account](variables.tf#L39) | Billing account id. If billing account is not part of the same org set `is_org_level` to `false`. To disable handling of billing IAM roles set `no_iam` to `true`. | object({…}) | ✓ | | 0-bootstrap | | [organization](variables.tf#L192) | Organization details. | object({…}) | ✓ | | 0-bootstrap | | [prefix](variables.tf#L216) | Prefix used for resources that need unique names. Use 9 characters or less. | string | ✓ | | 0-bootstrap | diff --git a/fast/stages/1-resman/outputs.tf b/fast/stages/1-resman/outputs.tf index 552d42d7..41994e98 100644 --- a/fast/stages/1-resman/outputs.tf +++ b/fast/stages/1-resman/outputs.tf @@ -62,8 +62,8 @@ locals { for k, v in local.cicd_repositories : k => templatefile( "${path.module}/templates/workflow-${v.type}.yaml", merge(local.cicd_workflow_attrs[k], { - audience = try( - local.identity_providers[v.identity_provider].audience, null + audiences = try( + local.identity_providers[v.identity_provider].audiences, null ) identity_provider = try( local.identity_providers[v.identity_provider].name, null diff --git a/fast/stages/1-resman/templates/workflow-gitlab.yaml b/fast/stages/1-resman/templates/workflow-gitlab.yaml index 33e55d21..13057e11 100644 --- a/fast/stages/1-resman/templates/workflow-gitlab.yaml +++ b/fast/stages/1-resman/templates/workflow-gitlab.yaml @@ -20,13 +20,14 @@ default: - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" variables: - AUDIENCE: ${audience} GOOGLE_CREDENTIALS: cicd-sa-credentials.json FAST_OUTPUTS_BUCKET: ${outputs_bucket} FAST_SERVICE_ACCOUNT: ${service_account} FAST_WIF_PROVIDER: ${identity_provider} SSH_AUTH_SOCK: /tmp/ssh_agent.sock + %{~ if tf_providers_file != "" ~} TF_PROVIDERS_FILE: ${tf_providers_file} + %{~ endif ~} TF_VAR_FILES: ${tf_var_files == [] ? "''" : join("\n ", tf_var_files)} stages: @@ -38,16 +39,27 @@ stages: cache: key: gcp-auth paths: - - .tf-setup + - cicd-sa-credentials.json + - token.txt + %{~ if tf_providers_file != "" ~} + - ${tf_providers_file} + %{~ endif ~} + %{~ for f in tf_var_files ~} + - ${f} + %{~ endfor ~} gcp-auth: - stage: gcp-auth id_tokens: GITLAB_TOKEN: - aud: "$${AUDIENCE}" + aud: + %{~ for aud in audiences ~} + - ${aud} + %{~ endfor ~} image: name: google/cloud-sdk:slim + stage: gcp-auth script: + - echo "$${GITLAB_TOKEN}" > token.txt - | gcloud iam workload-identity-pools create-cred-config \ $${FAST_WIF_PROVIDER} \ @@ -55,30 +67,27 @@ gcp-auth: --service-account-token-lifetime-seconds=3600 \ --output-file=$${GOOGLE_CREDENTIALS} \ --credential-source-file=token.txt - - rm token.txt - artifacts: - untracked: true tf-files: - stage: tf-files + dependencies: + - gcp-auth image: name: google/cloud-sdk:slim + stage: tf-files script: # - gcloud components install -q alpha - gcloud config set auth/credential_file_override $${GOOGLE_CREDENTIALS} - - mkdir -p .tf-setup - - | - gcloud alpha storage cp -r \ - "gs://$${FAST_OUTPUTS_BUCKET}/providers/$${TF_PROVIDERS_FILE}" .tf-setup/ - - | - gcloud alpha storage cp -r \ - "gs://$${FAST_OUTPUTS_BUCKET}/tfvars" .tf-setup/ - artifacts: - untracked: true - dependencies: - - gcp-auth + %{~ if tf_providers_file != "" ~} + - gcloud alpha storage cp -r "gs://$${FAST_OUTPUTS_BUCKET}/providers/${tf_providers_file}" ./ + %{~ endif ~} + %{~ for f in tf_var_files ~} + - gcloud alpha storage cp -r "gs://$${FAST_OUTPUTS_BUCKET}/tfvars/${f}" ./ + %{~ endfor ~} + - ls -l tf-plan: + dependencies: + - tf-files stage: tf-plan # uncomment the following lines and set the SSH key secret for private modules repo # before_script: @@ -89,20 +98,13 @@ tf-plan: # ssh-keyscan -H 'gitlab.com' >> ~/.ssh/known_hosts # ssh-keyscan gitlab.com | sort -u - ~/.ssh/known_hosts -o ~/.ssh/known_hosts script: - - cp ".tf-setup/$${TF_PROVIDERS_FILE}" ./ - - | - for f in "$${TF_VAR_FILES}"; do - ln -s ".tf-setup/tfvars/$f" ./ - done - terraform init - terraform validate - terraform plan - artifacts: - untracked: true - dependencies: - - tf-files tf-apply: + dependencies: + - tf-files stage: tf-apply # uncomment the following lines and set the SSH key secret for private modules repo # before_script: @@ -113,18 +115,9 @@ tf-apply: # ssh-keyscan -H 'gitlab.com' >> ~/.ssh/known_hosts # ssh-keyscan gitlab.com | sort -u - ~/.ssh/known_hosts -o ~/.ssh/known_hosts script: - - cp .tf-setup/$${TF_PROVIDERS_FILE} ./ - - | - for f in $${TF_VAR_FILES}; do - ln -s ".tf-setup/tfvars/$f" ./ - done - terraform init - terraform validate - terraform apply -input=false -auto-approve - artifacts: - untracked: true - dependencies: - - tf-files when: manual only: variables: diff --git a/fast/stages/1-resman/variables.tf b/fast/stages/1-resman/variables.tf index a613e5ad..f255423a 100644 --- a/fast/stages/1-resman/variables.tf +++ b/fast/stages/1-resman/variables.tf @@ -26,7 +26,7 @@ variable "automation" { project_number = string federated_identity_pool = string federated_identity_providers = map(object({ - audience = string + audiences = list(string) issuer = string issuer_uri = string name = string