Fix FAST CI/CD for Gitlab (#1593)
* fix cicd (multitenant untested) * tfdoc * rename allowed_audiences to audiences, align multitenant
This commit is contained in:
parent
b6b660f4f3
commit
2423fd40c1
|
@ -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:
|
||||
|
|
|
@ -175,7 +175,6 @@ This configuration is possible but unsupported and only exists for development p
|
|||
|
||||
<!-- TFDOC OPTS files:1 show_extra:1 -->
|
||||
<!-- BEGIN TFDOC -->
|
||||
|
||||
## 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. | <code title="object({ outputs_bucket = string project_id = string project_number = string federated_identity_pool = string federated_identity_providers = map(object({ issuer = string issuer_uri = string name = string principal_tpl = string principalset_tpl = string })) })">object({…})</code> | ✓ | | <code>0-bootstrap</code> |
|
||||
| [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`. | <code title="object({ id = string is_org_level = optional(bool, true) no_iam = optional(bool, false) })">object({…})</code> | ✓ | | |
|
||||
| [organization](variables.tf#L191) | Organization details. | <code title="object({ domain = string id = number customer_id = string })">object({…})</code> | ✓ | | <code>0-bootstrap</code> |
|
||||
| [prefix](variables.tf#L207) | Prefix used for resources that need unique names. Use 9 characters or less. | <code>string</code> | ✓ | | <code>0-bootstrap</code> |
|
||||
| [tag_keys](variables.tf#L230) | Organization tag keys. | <code title="object({ context = string environment = string tenant = string })">object({…})</code> | ✓ | | <code>1-resman</code> |
|
||||
| [tag_names](variables.tf#L241) | Customized names for resource management tags. | <code title="object({ context = string environment = string tenant = string })">object({…})</code> | ✓ | | <code>1-resman</code> |
|
||||
| [tag_values](variables.tf#L252) | Organization resource management tag values. | <code>map(string)</code> | ✓ | | <code>1-resman</code> |
|
||||
| [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. | <code title="object({ descriptive_name = string groups = object({ gcp-admins = string gcp-devops = optional(string) gcp-network-admins = optional(string) gcp-security-admins = optional(string) }) short_name = string short_name_is_prefix = optional(bool, false) fast_features = optional(object({ data_platform = optional(bool) gke = optional(bool) project_factory = optional(bool) sandbox = optional(bool) teams = optional(bool) }), {}) locations = optional(object({ bq = optional(string) gcs = optional(string) logging = optional(string) pubsub = optional(list(string)) }), {}) })">object({…})</code> | ✓ | | |
|
||||
| [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. | <code title="object({ bootstrap = optional(object({ branch = optional(string) identity_provider = string name = string type = string })) resman = optional(object({ branch = optional(string) identity_provider = string name = string type = string })) })">object({…})</code> | | <code>null</code> | |
|
||||
| [custom_roles](variables.tf#L94) | Custom roles defined at the organization level, in key => id format. | <code title="object({ service_project_network_admin = string tenant_network_admin = string })">object({…})</code> | | <code>null</code> | <code>0-bootstrap</code> |
|
||||
| [fast_features](variables.tf#L104) | Selective control for top-level FAST features. | <code title="object({ data_platform = optional(bool, true) gke = optional(bool, true) project_factory = optional(bool, true) sandbox = optional(bool, true) teams = optional(bool, true) })">object({…})</code> | | <code>{}</code> | <code>0-bootstrap</code> |
|
||||
| [federated_identity_providers](variables.tf#L118) | Workload Identity Federation pools. The `cicd_repositories` variable references keys here. | <code title="map(object({ attribute_condition = string issuer = string custom_settings = object({ issuer_uri = string allowed_audiences = list(string) }) }))">map(object({…}))</code> | | <code>{}</code> | |
|
||||
| [group_iam](variables.tf#L132) | Tenant-level custom group IAM settings in group => [roles] format. | <code>map(list(string))</code> | | <code>{}</code> | |
|
||||
| [iam](variables.tf#L138) | Tenant-level custom IAM settings in role => [principal] format. | <code>map(list(string))</code> | | <code>{}</code> | |
|
||||
| [iam_additive](variables.tf#L144) | Tenant-level custom IAM settings in role => [principal] format for non-authoritative bindings. | <code>map(list(string))</code> | | <code>{}</code> | |
|
||||
| [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. | <code title="object({ bq = string gcs = string logging = string pubsub = list(string) })">object({…})</code> | | <code title="{ bq = "EU" gcs = "EU" logging = "global" pubsub = [] }">{…}</code> | <code>0-bootstrap</code> |
|
||||
| [log_sinks](variables.tf#L170) | Tenant-level log sinks, in name => {type, filter} format. | <code title="map(object({ filter = string type = string }))">map(object({…}))</code> | | <code title="{ audit-logs = { filter = "logName:\"/logs/cloudaudit.googleapis.com%2Factivity\" OR logName:\"/logs/cloudaudit.googleapis.com%2Fsystem_event\"" type = "logging" } }">{…}</code> | |
|
||||
| [outputs_location](variables.tf#L201) | Enable writing provider, tfvars and CI/CD workflow files to local filesystem. Leave null to disable. | <code>string</code> | | <code>null</code> | |
|
||||
| [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. | <code title="object({ automation = string logging = string })">object({…})</code> | | <code title="{ automation = null logging = null }">{…}</code> | |
|
||||
| [test_principal](variables.tf#L300) | Used when testing to bypass the data source returning the current identity. | <code>string</code> | | <code>null</code> | |
|
||||
| [automation](variables.tf#L20) | Automation resources created by the organization-level bootstrap stage. | <code title="object({ outputs_bucket = string project_id = string project_number = string federated_identity_pool = string federated_identity_providers = map(object({ audiences = list(string) issuer = string issuer_uri = string name = string principal_tpl = string principalset_tpl = string })) })">object({…})</code> | ✓ | | <code>0-bootstrap</code> |
|
||||
| [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`. | <code title="object({ id = string is_org_level = optional(bool, true) no_iam = optional(bool, false) })">object({…})</code> | ✓ | | |
|
||||
| [organization](variables.tf#L192) | Organization details. | <code title="object({ domain = string id = number customer_id = string })">object({…})</code> | ✓ | | <code>0-bootstrap</code> |
|
||||
| [prefix](variables.tf#L208) | Prefix used for resources that need unique names. Use 9 characters or less. | <code>string</code> | ✓ | | <code>0-bootstrap</code> |
|
||||
| [tag_keys](variables.tf#L231) | Organization tag keys. | <code title="object({ context = string environment = string tenant = string })">object({…})</code> | ✓ | | <code>1-resman</code> |
|
||||
| [tag_names](variables.tf#L242) | Customized names for resource management tags. | <code title="object({ context = string environment = string tenant = string })">object({…})</code> | ✓ | | <code>1-resman</code> |
|
||||
| [tag_values](variables.tf#L253) | Organization resource management tag values. | <code>map(string)</code> | ✓ | | <code>1-resman</code> |
|
||||
| [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. | <code title="object({ descriptive_name = string groups = object({ gcp-admins = string gcp-devops = optional(string) gcp-network-admins = optional(string) gcp-security-admins = optional(string) }) short_name = string short_name_is_prefix = optional(bool, false) fast_features = optional(object({ data_platform = optional(bool) gke = optional(bool) project_factory = optional(bool) sandbox = optional(bool) teams = optional(bool) }), {}) locations = optional(object({ bq = optional(string) gcs = optional(string) logging = optional(string) pubsub = optional(list(string)) }), {}) })">object({…})</code> | ✓ | | |
|
||||
| [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. | <code title="object({ bootstrap = optional(object({ branch = optional(string) identity_provider = string name = string type = string })) resman = optional(object({ branch = optional(string) identity_provider = string name = string type = string })) })">object({…})</code> | | <code>null</code> | |
|
||||
| [custom_roles](variables.tf#L95) | Custom roles defined at the organization level, in key => id format. | <code title="object({ service_project_network_admin = string tenant_network_admin = string })">object({…})</code> | | <code>null</code> | <code>0-bootstrap</code> |
|
||||
| [fast_features](variables.tf#L105) | Selective control for top-level FAST features. | <code title="object({ data_platform = optional(bool, true) gke = optional(bool, true) project_factory = optional(bool, true) sandbox = optional(bool, true) teams = optional(bool, true) })">object({…})</code> | | <code>{}</code> | <code>0-bootstrap</code> |
|
||||
| [federated_identity_providers](variables.tf#L119) | Workload Identity Federation pools. The `cicd_repositories` variable references keys here. | <code title="map(object({ attribute_condition = optional(string) issuer = string custom_settings = optional(object({ issuer_uri = optional(string) audiences = optional(list(string), []) }), {}) }))">map(object({…}))</code> | | <code>{}</code> | |
|
||||
| [group_iam](variables.tf#L133) | Tenant-level custom group IAM settings in group => [roles] format. | <code>map(list(string))</code> | | <code>{}</code> | |
|
||||
| [iam](variables.tf#L139) | Tenant-level custom IAM settings in role => [principal] format. | <code>map(list(string))</code> | | <code>{}</code> | |
|
||||
| [iam_additive](variables.tf#L145) | Tenant-level custom IAM settings in role => [principal] format for non-authoritative bindings. | <code>map(list(string))</code> | | <code>{}</code> | |
|
||||
| [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. | <code title="object({ bq = string gcs = string logging = string pubsub = list(string) })">object({…})</code> | | <code title="{ bq = "EU" gcs = "EU" logging = "global" pubsub = [] }">{…}</code> | <code>0-bootstrap</code> |
|
||||
| [log_sinks](variables.tf#L171) | Tenant-level log sinks, in name => {type, filter} format. | <code title="map(object({ filter = string type = string }))">map(object({…}))</code> | | <code title="{ audit-logs = { filter = "logName:\"/logs/cloudaudit.googleapis.com%2Factivity\" OR logName:\"/logs/cloudaudit.googleapis.com%2Fsystem_event\"" type = "logging" } }">{…}</code> | |
|
||||
| [outputs_location](variables.tf#L202) | Enable writing provider, tfvars and CI/CD workflow files to local filesystem. Leave null to disable. | <code>string</code> | | <code>null</code> | |
|
||||
| [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. | <code title="object({ automation = string logging = string })">object({…})</code> | | <code title="{ automation = null logging = null }">{…}</code> | |
|
||||
| [test_principal](variables.tf#L301) | Used when testing to bypass the data source returning the current identity. | <code>string</code> | | <code>null</code> | |
|
||||
|
||||
## 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. | ✓ | <code>stage-01</code> |
|
||||
| [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. | ✓ | <code>stage-01</code> |
|
||||
| [tenant_resources](outputs.tf#L132) | Tenant-level resources. | | |
|
||||
| [tfvars](outputs.tf#L143) | Terraform variable files for the following tenant stages. | ✓ | |
|
||||
<!-- END TFDOC -->
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,7 +56,6 @@ 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"
|
||||
|
@ -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)
|
||||
)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -126,7 +126,6 @@ Once the configuration is done just go through the usual `init/apply` cycle. On
|
|||
|
||||
<!-- TFDOC OPTS files:1 show_extra:1 -->
|
||||
<!-- BEGIN TFDOC -->
|
||||
|
||||
## 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. | <code title="object({ outputs_bucket = string project_id = string project_number = string federated_identity_pools = list(string) federated_identity_providers = map(object({ issuer = string issuer_uri = string name = string principal_tpl = string principalset_tpl = string })) service_accounts = object({ networking = string resman = string security = string dp-dev = optional(string) dp-prod = optional(string) gke-dev = optional(string) gke-prod = optional(string) pf-dev = optional(string) pf-prod = optional(string) sandbox = optional(string) teams = optional(string) }) })">object({…})</code> | ✓ | | <code>0-bootstrap</code> |
|
||||
| [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`. | <code title="object({ id = string is_org_level = optional(bool, true) no_iam = optional(bool, false) })">object({…})</code> | ✓ | | <code>0-bootstrap</code> |
|
||||
| [organization](variables.tf#L204) | Organization details. | <code title="object({ domain = string id = number customer_id = string })">object({…})</code> | ✓ | | <code>0-bootstrap</code> |
|
||||
| [prefix](variables.tf#L226) | Prefix used for resources that need unique names. Use 9 characters or less. | <code>string</code> | ✓ | | <code>0-bootstrap</code> |
|
||||
| [automation](variables.tf#L20) | Automation resources created by the bootstrap stage. | <code title="object({ outputs_bucket = string project_id = string project_number = string federated_identity_pools = list(string) federated_identity_providers = map(object({ audiences = list(string) issuer = string issuer_uri = string name = string principal_tpl = string principalset_tpl = string })) service_accounts = object({ networking = string resman = string security = string dp-dev = optional(string) dp-prod = optional(string) gke-dev = optional(string) gke-prod = optional(string) pf-dev = optional(string) pf-prod = optional(string) sandbox = optional(string) teams = optional(string) }) })">object({…})</code> | ✓ | | <code>0-bootstrap</code> |
|
||||
| [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`. | <code title="object({ id = string is_org_level = optional(bool, true) no_iam = optional(bool, false) })">object({…})</code> | ✓ | | <code>0-bootstrap</code> |
|
||||
| [organization](variables.tf#L205) | Organization details. | <code title="object({ domain = string id = number customer_id = string })">object({…})</code> | ✓ | | <code>0-bootstrap</code> |
|
||||
| [prefix](variables.tf#L227) | Prefix used for resources that need unique names. Use 9 characters or less. | <code>string</code> | ✓ | | <code>0-bootstrap</code> |
|
||||
| [root_node](variables.tf#L237) | Root folder node for the tenant, in folders/nnnnnn format. | <code>string</code> | ✓ | | |
|
||||
| [short_name](variables.tf#L242) | Short name used to identify the tenant. | <code>string</code> | ✓ | | |
|
||||
| [tags](variables.tf#L247) | Resource management tags. | <code title="object({ keys = object({ context = string environment = string tenant = string }) names = object({ context = string environment = string tenant = string }) values = map(string) })">object({…})</code> | ✓ | | |
|
||||
| [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. | <code title="object({ data_platform_dev = object({ branch = string identity_provider = string name = string type = string }) data_platform_prod = object({ branch = string identity_provider = string name = string type = string }) gke_dev = object({ branch = string identity_provider = string name = string type = string }) gke_prod = object({ branch = string identity_provider = string name = string type = string }) networking = object({ branch = string identity_provider = string name = string type = string }) project_factory_dev = object({ branch = string identity_provider = string name = string type = string }) project_factory_prod = object({ branch = string identity_provider = string name = string type = string }) security = object({ branch = string identity_provider = string name = string type = string }) })">object({…})</code> | | <code>null</code> | |
|
||||
| [custom_roles](variables.tf#L144) | Custom roles defined at the org level, in key => id format. | <code title="object({ service_project_network_admin = string })">object({…})</code> | | <code>null</code> | <code>0-bootstrap</code> |
|
||||
| [data_dir](variables.tf#L153) | Relative path for the folder storing configuration data. | <code>string</code> | | <code>"data"</code> | |
|
||||
| [fast_features](variables.tf#L159) | Selective control for top-level FAST features. | <code title="object({ data_platform = optional(bool, false) gke = optional(bool, false) project_factory = optional(bool, false) sandbox = optional(bool, false) teams = optional(bool, false) })">object({…})</code> | | <code>{}</code> | <code>0-0-bootstrap</code> |
|
||||
| [groups](variables.tf#L173) | Group names to grant organization-level permissions. | <code title="object({ gcp-devops = optional(string) gcp-network-admins = optional(string) gcp-security-admins = optional(string) })">object({…})</code> | | <code>{}</code> | <code>0-bootstrap</code> |
|
||||
| [locations](variables.tf#L186) | Optional locations for GCS, BigQuery, and logging buckets created here. | <code title="object({ bq = string gcs = string logging = string pubsub = list(string) })">object({…})</code> | | <code title="{ bq = "EU" gcs = "EU" logging = "global" pubsub = [] }">{…}</code> | <code>0-bootstrap</code> |
|
||||
| [organization_policy_data_path](variables.tf#L214) | Path for the data folder used by the organization policies factory. | <code>string</code> | | <code>null</code> | |
|
||||
| [outputs_location](variables.tf#L220) | Enable writing provider, tfvars and CI/CD workflow files to local filesystem. Leave null to disable. | <code>string</code> | | <code>null</code> | |
|
||||
| [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. | <code title="object({ data_platform_dev = object({ branch = string identity_provider = string name = string type = string }) data_platform_prod = object({ branch = string identity_provider = string name = string type = string }) gke_dev = object({ branch = string identity_provider = string name = string type = string }) gke_prod = object({ branch = string identity_provider = string name = string type = string }) networking = object({ branch = string identity_provider = string name = string type = string }) project_factory_dev = object({ branch = string identity_provider = string name = string type = string }) project_factory_prod = object({ branch = string identity_provider = string name = string type = string }) security = object({ branch = string identity_provider = string name = string type = string }) })">object({…})</code> | | <code>null</code> | |
|
||||
| [custom_roles](variables.tf#L145) | Custom roles defined at the org level, in key => id format. | <code title="object({ service_project_network_admin = string })">object({…})</code> | | <code>null</code> | <code>0-bootstrap</code> |
|
||||
| [data_dir](variables.tf#L154) | Relative path for the folder storing configuration data. | <code>string</code> | | <code>"data"</code> | |
|
||||
| [fast_features](variables.tf#L160) | Selective control for top-level FAST features. | <code title="object({ data_platform = optional(bool, false) gke = optional(bool, false) project_factory = optional(bool, false) sandbox = optional(bool, false) teams = optional(bool, false) })">object({…})</code> | | <code>{}</code> | <code>0-0-bootstrap</code> |
|
||||
| [groups](variables.tf#L174) | Group names to grant organization-level permissions. | <code title="object({ gcp-devops = optional(string) gcp-network-admins = optional(string) gcp-security-admins = optional(string) })">object({…})</code> | | <code>{}</code> | <code>0-bootstrap</code> |
|
||||
| [locations](variables.tf#L187) | Optional locations for GCS, BigQuery, and logging buckets created here. | <code title="object({ bq = string gcs = string logging = string pubsub = list(string) })">object({…})</code> | | <code title="{ bq = "EU" gcs = "EU" logging = "global" pubsub = [] }">{…}</code> | <code>0-bootstrap</code> |
|
||||
| [organization_policy_data_path](variables.tf#L215) | Path for the data folder used by the organization policies factory. | <code>string</code> | | <code>null</code> | |
|
||||
| [outputs_location](variables.tf#L221) | Enable writing provider, tfvars and CI/CD workflow files to local filesystem. Leave null to disable. | <code>string</code> | | <code>null</code> | |
|
||||
| [team_folders](variables.tf#L265) | Team folders to be created. Format is described in a code comment. | <code title="map(object({ descriptive_name = string group_iam = map(list(string)) impersonation_groups = list(string) }))">map(object({…}))</code> | | <code>null</code> | |
|
||||
| [test_skip_data_sources](variables.tf#L275) | Used when testing to bypass data sources. | <code>bool</code> | | <code>false</code> | |
|
||||
|
||||
|
@ -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. | | <code>03-gke-multitenant</code> |
|
||||
| [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. | ✓ | <code>02-networking</code> · <code>02-security</code> · <code>03-dataplatform</code> · <code>xx-sandbox</code> · <code>xx-teams</code> |
|
||||
| [sandbox](outputs.tf#L271) | Data for the sandbox stage. | | <code>xx-sandbox</code> |
|
||||
| [security](outputs.tf#L285) | Data for the networking stage. | | <code>02-security</code> |
|
||||
| [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. | | <code>03-gke-multitenant</code> |
|
||||
| [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. | ✓ | <code>02-networking</code> · <code>02-security</code> · <code>03-dataplatform</code> · <code>xx-sandbox</code> · <code>xx-teams</code> |
|
||||
| [sandbox](outputs.tf#L274) | Data for the sandbox stage. | | <code>xx-sandbox</code> |
|
||||
| [security](outputs.tf#L288) | Data for the networking stage. | | <code>02-security</code> |
|
||||
| [teams](outputs.tf#L298) | Data for the teams stage. | | |
|
||||
| [tfvars](outputs.tf#L310) | Terraform variable files for the following stages. | ✓ | |
|
||||
<!-- END TFDOC -->
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)."
|
||||
|
|
|
@ -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. | <code title="object({ organization_iam_admin = string service_project_network_admin = string tenant_network_admin = string })">object({…})</code> | | <code title="{ organization_iam_admin = "organizationIamAdmin" service_project_network_admin = "serviceProjectNetworkAdmin" tenant_network_admin = "tenantNetworkAdmin" }">{…}</code> | |
|
||||
| [custom_roles](variables.tf#L93) | Map of role names => list of permissions to additionally create at the organization level. | <code>map(list(string))</code> | | <code>{}</code> | |
|
||||
| [fast_features](variables.tf#L100) | Selective control for top-level FAST features. | <code title="object({ data_platform = optional(bool, false) gke = optional(bool, false) project_factory = optional(bool, false) sandbox = optional(bool, false) teams = optional(bool, false) })">object({…})</code> | | <code>{}</code> | |
|
||||
| [federated_identity_providers](variables.tf#L113) | Workload Identity Federation pools. The `cicd_repositories` variable references keys here. | <code title="map(object({ attribute_condition = optional(string) issuer = string custom_settings = optional(object({ issuer_uri = optional(string) allowed_audiences = optional(list(string), []) }), {}) }))">map(object({…}))</code> | | <code>{}</code> | |
|
||||
| [federated_identity_providers](variables.tf#L113) | Workload Identity Federation pools. The `cicd_repositories` variable references keys here. | <code title="map(object({ attribute_condition = optional(string) issuer = string custom_settings = optional(object({ issuer_uri = optional(string) audiences = optional(list(string), []) }), {}) }))">map(object({…}))</code> | | <code>{}</code> | |
|
||||
| [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. | <code>map(string)</code> | | <code title="{ gcp-billing-admins = "gcp-billing-admins", gcp-devops = "gcp-devops", gcp-network-admins = "gcp-network-admins" gcp-organization-admins = "gcp-organization-admins" gcp-security-admins = "gcp-security-admins" gcp-support = "gcp-devops" }">{…}</code> | |
|
||||
| [iam](variables.tf#L150) | Organization-level custom IAM settings in role => [principal] format. | <code>map(list(string))</code> | | <code>{}</code> | |
|
||||
| [iam_additive](variables.tf#L156) | Organization-level custom IAM settings in role => [principal] format for non-authoritative bindings. | <code>map(list(string))</code> | | <code>{}</code> | |
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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, ""
|
||||
)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -117,7 +117,7 @@ variable "federated_identity_providers" {
|
|||
issuer = string
|
||||
custom_settings = optional(object({
|
||||
issuer_uri = optional(string)
|
||||
allowed_audiences = optional(list(string), [])
|
||||
audiences = optional(list(string), [])
|
||||
}), {})
|
||||
}))
|
||||
default = {}
|
||||
|
|
|
@ -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. | <code title="object({ outputs_bucket = string project_id = string project_number = string federated_identity_pool = string federated_identity_providers = map(object({ audience = string issuer = string issuer_uri = string name = string principal_tpl = string principalset_tpl = string })) })">object({…})</code> | ✓ | | <code>0-bootstrap</code> |
|
||||
| [automation](variables.tf#L20) | Automation resources created by the bootstrap stage. | <code title="object({ outputs_bucket = string project_id = string project_number = string federated_identity_pool = string federated_identity_providers = map(object({ audiences = list(string) issuer = string issuer_uri = string name = string principal_tpl = string principalset_tpl = string })) })">object({…})</code> | ✓ | | <code>0-bootstrap</code> |
|
||||
| [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`. | <code title="object({ id = string is_org_level = optional(bool, true) no_iam = optional(bool, false) })">object({…})</code> | ✓ | | <code>0-bootstrap</code> |
|
||||
| [organization](variables.tf#L192) | Organization details. | <code title="object({ domain = string id = number customer_id = string })">object({…})</code> | ✓ | | <code>0-bootstrap</code> |
|
||||
| [prefix](variables.tf#L216) | Prefix used for resources that need unique names. Use 9 characters or less. | <code>string</code> | ✓ | | <code>0-bootstrap</code> |
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue