diff --git a/.gitignore b/.gitignore
index ed88b00d..cbf110dd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,6 +10,7 @@
**/.test.lock
.idea
.vscode
+.idx/dev.nix
backend.tf
backend-config.hcl
credentials.json
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f05cd212..a8e5a07f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,10 +8,12 @@ All notable changes to this project will be documented in this file.
### FAST
+- [[#1593](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1593)] Fix FAST CI/CD for Gitlab ([ludoo](https://github.com/ludoo))
- [[#1583](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1583)] Fix module path for teams cicd ([ludoo](https://github.com/ludoo))
### MODULES
+- [[#1591](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1591)] feat: ๐ธ (modules/cloudsql-instance):add project_id for ssl cert ([erabusi](https://github.com/erabusi))
- [[#1589](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1589)] Add new `iam_members` variable to IAM additive module interfaces ([ludoo](https://github.com/ludoo))
- [[#1588](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1588)] feat: ๐ธ (modules/cloudsql-instance): enable require_ssl cert support ([erabusi](https://github.com/erabusi))
- [[#1587](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1587)] **incompatible change:** Fix factory rules key in net firewall policy module ([ludoo](https://github.com/ludoo))
diff --git a/blueprints/data-solutions/data-playground/README.md b/blueprints/data-solutions/data-playground/README.md
index c3693ee2..413445d4 100644
--- a/blueprints/data-solutions/data-playground/README.md
+++ b/blueprints/data-solutions/data-playground/README.md
@@ -24,7 +24,7 @@ If the network_config variable is not provided, one VPC will be created in each
## Deploy your environment
-We assume the identiy running the following steps has the following role:
+We assume the identity running the following steps has the following role:
- resourcemanager.projectCreator in case a new project will be created.
- owner on the project in case you use an existing project.
diff --git a/blueprints/gke/binauthz/README.md b/blueprints/gke/binauthz/README.md
index 740982e6..d4aa5f49 100644
--- a/blueprints/gke/binauthz/README.md
+++ b/blueprints/gke/binauthz/README.md
@@ -36,7 +36,7 @@ Once the resources have been created, do the following to verify that everything
kubectl apply -f tenant-setup.yaml
- By applying that manifest thw following is created:
+ By applying that manifest the following is created:
* A namespace called "apis". This is the namespace where the application will be deployed.
* A Role and a RoleBinding in previously created namespace so the service account that has been configured for the CD pipeline trigger in Cloud Build is able to deploy the kubernetes application to that namespace.
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..ffaeb17d 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))
| | {}
| |
@@ -529,14 +529,14 @@ The remaining configuration is manual, as it regards the repositories themselves
| name | description | sensitive | consumers |
|---|---|:---:|---|
-| [automation](outputs.tf#L100) | Automation resources. | | |
-| [billing_dataset](outputs.tf#L105) | BigQuery dataset prepared for billing export. | | |
-| [cicd_repositories](outputs.tf#L110) | CI/CD repository configurations. | | |
-| [custom_roles](outputs.tf#L122) | Organization-level custom roles. | | |
-| [federated_identity](outputs.tf#L127) | Workload Identity Federation pool and providers. | | |
-| [outputs_bucket](outputs.tf#L137) | GCS bucket where generated output files are stored. | | |
-| [project_ids](outputs.tf#L142) | Projects created by this stage. | | |
-| [providers](outputs.tf#L152) | Terraform provider files for this stage and dependent stages. | โ | stage-01
|
-| [service_accounts](outputs.tf#L159) | Automation service accounts created by this stage. | | |
-| [tfvars](outputs.tf#L168) | Terraform variable files for the following stages. | โ | |
+| [automation](outputs.tf#L102) | Automation resources. | | |
+| [billing_dataset](outputs.tf#L107) | BigQuery dataset prepared for billing export. | | |
+| [cicd_repositories](outputs.tf#L112) | CI/CD repository configurations. | | |
+| [custom_roles](outputs.tf#L124) | Organization-level custom roles. | | |
+| [federated_identity](outputs.tf#L129) | Workload Identity Federation pool and providers. | | |
+| [outputs_bucket](outputs.tf#L139) | GCS bucket where generated output files are stored. | | |
+| [project_ids](outputs.tf#L144) | Projects created by this stage. | | |
+| [providers](outputs.tf#L154) | Terraform provider files for this stage and dependent stages. | โ | stage-01
|
+| [service_accounts](outputs.tf#L161) | Automation service accounts created by this stage. | | |
+| [tfvars](outputs.tf#L170) | Terraform variable files for the following stages. | โ | |
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..70d84449 100644
--- a/fast/stages/0-bootstrap/outputs.tf
+++ b/fast/stages/0-bootstrap/outputs.tf
@@ -22,7 +22,9 @@ 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 = try(
+ 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
diff --git a/fast/stages/2-networking-c-nva/README.md b/fast/stages/2-networking-c-nva/README.md
index a587d68c..778de036 100644
--- a/fast/stages/2-networking-c-nva/README.md
+++ b/fast/stages/2-networking-c-nva/README.md
@@ -83,7 +83,7 @@ By default, the design assumes the following:
- cross-environment traffic and traffic from any untrusted network to any trusted network (and vice versa) pass through the NVAs. For demo purposes, the current NVA performs simple routing/natting only
- any traffic from a trusted network to an untrusted network (e.g. Internet) is natted by the NVAs. Users can configure further exclusions
-The trusted landing VPC acts as a hub: it bridges internal resources with the outside world and it hosts the shared services consumed by the spoke VPCs, connected to the hub thorugh VPC network peerings. Spokes are used to partition the environments. By default:
+The trusted landing VPC acts as a hub: it bridges internal resources with the outside world and it hosts the shared services consumed by the spoke VPCs, connected to the hub through VPC network peerings. Spokes are used to partition the environments. By default:
- one spoke VPC hosts the development environment resources
- one spoke VPC hosts the production environment resources
diff --git a/fast/stages/2-networking-e-nva-bgp/README.md b/fast/stages/2-networking-e-nva-bgp/README.md
index a9f444e1..32be44b1 100644
--- a/fast/stages/2-networking-e-nva-bgp/README.md
+++ b/fast/stages/2-networking-e-nva-bgp/README.md
@@ -37,6 +37,7 @@ The final number of subnets, and their IP addressing will depend on the user-spe
- [Design overview and choices](#design-overview-and-choices)
- [Multi-regional deployment](#multi-regional-deployment)
- [VPC design](#vpc-design)
+ - [NCC, NVAs and BGP sessions](#ncc-nvas-and-bgp-sessions)
- [External connectivity](#external-connectivity)
- [Internal connectivity](#internal-connectivity)
- [IP ranges, subnetting, routing](#ip-ranges-subnetting-routing)
@@ -105,7 +106,7 @@ By default, the design assumes that:
- cross-spoke (environment) traffic and traffic from any untrusted network to any trusted network (and vice versa) pass through the NVAs.
- any traffic from a trusted network to an untrusted network (e.g. Internet) is natted by the NVAs. Users can configure further exclusions.
-The trusted landing VPC acts as a hub: it bridges internal resources with the outside world and it hosts the shared services consumed by the spoke VPCs, connected to the hub thorugh VPC network peerings. Spokes are used to partition the environments. By default:
+The trusted landing VPC acts as a hub: it bridges internal resources with the outside world and it hosts the shared services consumed by the spoke VPCs, connected to the hub through VPC network peerings. Spokes are used to partition the environments. By default:
- one spoke VPC hosts the development environment resources
- one spoke VPC hosts the production environment resources
diff --git a/fast/stages/3-data-platform/dev/README.md b/fast/stages/3-data-platform/dev/README.md
index 2d1e5597..9836b598 100644
--- a/fast/stages/3-data-platform/dev/README.md
+++ b/fast/stages/3-data-platform/dev/README.md
@@ -95,12 +95,12 @@ The commands to link or copy the provider and terraform variable files can be ea
# copy and paste the following commands for '3-data-platform'
-ln -s /home/ludomagno/fast-config/providers/3-data-platform-providers.tf ./
-ln -s /home/ludomagno/fast-config/tfvars/globals.auto.tfvars.json ./
-ln -s /home/ludomagno/fast-config/tfvars/0-bootstrap.auto.tfvars.json ./
-ln -s /home/ludomagno/fast-config/tfvars/1-resman.auto.tfvars.json ./
-ln -s /home/ludomagno/fast-config/tfvars/2-networking.auto.tfvars.json ./
-ln -s /home/ludomagno/fast-config/tfvars/2-security.auto.tfvars.json ./
+ln -s ~/fast-config/providers/3-data-platform-providers.tf ./
+ln -s ~/fast-config/tfvars/globals.auto.tfvars.json ./
+ln -s ~/fast-config/tfvars/0-bootstrap.auto.tfvars.json ./
+ln -s ~/fast-config/tfvars/1-resman.auto.tfvars.json ./
+ln -s ~/fast-config/tfvars/2-networking.auto.tfvars.json ./
+ln -s ~/fast-config/tfvars/2-security.auto.tfvars.json ./
```
```bash
diff --git a/modules/bigtable-instance/README.md b/modules/bigtable-instance/README.md
index abc1509f..afe1ec42 100644
--- a/modules/bigtable-instance/README.md
+++ b/modules/bigtable-instance/README.md
@@ -246,7 +246,7 @@ module "bigtable-instance" {
| name | description | sensitive |
|---|---|:---:|
| [id](outputs.tf#L17) | Fully qualified instance id. | |
-| [instance](outputs.tf#L26) | BigTable intance. | |
+| [instance](outputs.tf#L26) | BigTable instance. | |
| [table_ids](outputs.tf#L35) | Map of fully qualified table ids keyed by table name. | |
| [tables](outputs.tf#L40) | Table resources. | |
diff --git a/modules/bigtable-instance/outputs.tf b/modules/bigtable-instance/outputs.tf
index 93f817dc..a2fd2646 100644
--- a/modules/bigtable-instance/outputs.tf
+++ b/modules/bigtable-instance/outputs.tf
@@ -24,7 +24,7 @@ output "id" {
}
output "instance" {
- description = "BigTable intance."
+ description = "BigTable instance."
value = google_bigtable_instance.default
depends_on = [
google_bigtable_instance_iam_binding.default,
diff --git a/modules/cloud-run/README.md b/modules/cloud-run/README.md
index 0db9d114..803ddcb6 100644
--- a/modules/cloud-run/README.md
+++ b/modules/cloud-run/README.md
@@ -352,10 +352,11 @@ module "cloud_run" {
| [revision_name](variables.tf#L177) | Revision name. | string
| | null
|
| [service_account](variables.tf#L183) | Service account email. Unused if service account is auto-created. | string
| | null
|
| [service_account_create](variables.tf#L189) | Auto-create service account. | bool
| | false
|
-| [timeout_seconds](variables.tf#L195) | Maximum duration the instance is allowed for responding to a request. | number
| | null
|
-| [traffic](variables.tf#L201) | Traffic steering configuration. If revision name is null the latest revision will be used. | map(object({…}))
| | {}
|
-| [volumes](variables.tf#L212) | Named volumes in containers in name => attributes format. | map(object({…}))
| | {}
|
-| [vpc_connector_create](variables.tf#L226) | Populate this to create a VPC connector. You can then refer to it in the template annotations. | object({…})
| | null
|
+| [startup_cpu_boost](variables.tf#L195) | Enable startup cpu boost. | bool
| | false
|
+| [timeout_seconds](variables.tf#L201) | Maximum duration the instance is allowed for responding to a request. | number
| | null
|
+| [traffic](variables.tf#L207) | Traffic steering configuration. If revision name is null the latest revision will be used. | map(object({…}))
| | {}
|
+| [volumes](variables.tf#L218) | Named volumes in containers in name => attributes format. | map(object({…}))
| | {}
|
+| [vpc_connector_create](variables.tf#L232) | Populate this to create a VPC connector. You can then refer to it in the template annotations. | object({…})
| | null
|
## Outputs
diff --git a/modules/cloud-run/main.tf b/modules/cloud-run/main.tf
index e5371273..527e1fd1 100644
--- a/modules/cloud-run/main.tf
+++ b/modules/cloud-run/main.tf
@@ -33,7 +33,7 @@ locals {
annotations = merge(
var.ingress_settings == null ? {} : {
"run.googleapis.com/ingress" = var.ingress_settings
- }
+ },
)
_iam_run_invoker_members = concat(
lookup(var.iam, "roles/run.invoker", []),
@@ -72,6 +72,9 @@ locals {
var.gen2_execution_environment ? {
"run.googleapis.com/execution-environment" = "gen2"
} : {},
+ var.startup_cpu_boost ? {
+ "run.googleapis.com/startup-cpu-boost" = "true"
+ } : {},
)
revision_name = (
try(var.revision_name, null) == null
diff --git a/modules/cloud-run/variables.tf b/modules/cloud-run/variables.tf
index 09d10296..9a4992a7 100644
--- a/modules/cloud-run/variables.tf
+++ b/modules/cloud-run/variables.tf
@@ -192,6 +192,12 @@ variable "service_account_create" {
default = false
}
+variable "startup_cpu_boost" {
+ description = "Enable startup cpu boost."
+ type = bool
+ default = false
+}
+
variable "timeout_seconds" {
description = "Maximum duration the instance is allowed for responding to a request."
type = number
diff --git a/modules/data-catalog-policy-tag/README.md b/modules/data-catalog-policy-tag/README.md
index 6f53fcbd..5a1c2055 100644
--- a/modules/data-catalog-policy-tag/README.md
+++ b/modules/data-catalog-policy-tag/README.md
@@ -66,18 +66,18 @@ module "cmn-dc" {
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
-| [name](variables.tf#L69) | Name of this taxonomy. | string
| โ | |
-| [project_id](variables.tf#L84) | GCP project id. |
| โ | |
+| [name](variables.tf#L74) | Name of this taxonomy. | string
| โ | |
+| [project_id](variables.tf#L89) | GCP project id. |
| โ | |
| [activated_policy_types](variables.tf#L17) | A list of policy types that are activated for this taxonomy. | list(string)
| | ["FINE_GRAINED_ACCESS_CONTROL"]
|
| [description](variables.tf#L23) | Description of this taxonomy. | string
| | "Taxonomy - Terraform managed"
|
| [group_iam](variables.tf#L29) | Authoritative IAM binding for organization groups, in {GROUP_EMAIL => [ROLES]} format. Group emails need to be static. Can be used in combination with the `iam` variable. | map(list(string))
| | {}
|
| [iam](variables.tf#L35) | IAM bindings in {ROLE => [MEMBERS]} format. | map(list(string))
| | {}
|
| [iam_additive](variables.tf#L41) | IAM additive bindings in {ROLE => [MEMBERS]} format. | map(list(string))
| | {}
|
| [iam_additive_members](variables.tf#L47) | IAM additive bindings in {MEMBERS => [ROLE]} format. This might break if members are dynamic values. | map(list(string))
| | {}
|
-| [iam_members](variables.tf#L53) | Individual additive IAM bindings, use this when iam_additive does not work due to dynamic resources. Keys are arbitrary and only used for the internal loop. | map(object({…}))
| | {}
|
-| [location](variables.tf#L63) | Data Catalog Taxonomy location. | string
| | "eu"
|
-| [prefix](variables.tf#L74) | Optional prefix used to generate project id and name. | string
| | null
|
-| [tags](variables.tf#L88) | List of Data Catalog Policy tags to be created with optional IAM binging configuration in {tag => {ROLE => [MEMBERS]}} format. | map(object({…}))
| | {}
|
+| [iam_members](variables.tf#L53) | Individual additive IAM bindings, use this when iam_additive does not work due to dynamic resources. Keys are arbitrary and only used for the internal loop. | map(object({…}))
| | {}
|
+| [location](variables.tf#L68) | Data Catalog Taxonomy location. | string
| | "eu"
|
+| [prefix](variables.tf#L79) | Optional prefix used to generate project id and name. | string
| | null
|
+| [tags](variables.tf#L93) | List of Data Catalog Policy tags to be created with optional IAM binging configuration in {tag => {ROLE => [MEMBERS]}} format. | map(object({…}))
| | {}
|
## Outputs
diff --git a/modules/data-catalog-policy-tag/iam.tf b/modules/data-catalog-policy-tag/iam.tf
index 6f79aebf..9e7f3a09 100644
--- a/modules/data-catalog-policy-tag/iam.tf
+++ b/modules/data-catalog-policy-tag/iam.tf
@@ -80,6 +80,14 @@ resource "google_data_catalog_taxonomy_iam_member" "members" {
taxonomy = google_data_catalog_taxonomy.default.id
role = each.value.role
member = each.value.member
+ dynamic "condition" {
+ for_each = each.value.condition == null ? [] : [""]
+ content {
+ expression = each.value.condition.expression
+ title = each.value.condition.title
+ description = each.value.condition.description
+ }
+ }
}
resource "google_data_catalog_policy_tag_iam_binding" "authoritative" {
diff --git a/modules/data-catalog-policy-tag/variables.tf b/modules/data-catalog-policy-tag/variables.tf
index c7c8eb07..2b799a96 100644
--- a/modules/data-catalog-policy-tag/variables.tf
+++ b/modules/data-catalog-policy-tag/variables.tf
@@ -55,6 +55,11 @@ variable "iam_members" {
type = map(object({
member = string
role = string
+ condition = optional(object({
+ expression = string
+ title = string
+ description = optional(string)
+ }))
}))
nullable = false
default = {}
diff --git a/modules/dataplex-datascan/README.md b/modules/dataplex-datascan/README.md
index 65436af6..ca8afca7 100644
--- a/modules/dataplex-datascan/README.md
+++ b/modules/dataplex-datascan/README.md
@@ -433,9 +433,9 @@ module "dataplex-datascan" {
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
| [data](variables.tf#L17) | The data source for DataScan. The source can be either a Dataplex `entity` or a BigQuery `resource`. | object({…})
| โ | |
-| [name](variables.tf#L156) | Name of Dataplex Scan. | string
| โ | |
-| [project_id](variables.tf#L167) | The ID of the project where the Dataplex DataScan will be created. | string
| โ | |
-| [region](variables.tf#L172) | Region for the Dataplex DataScan. | string
| โ | |
+| [name](variables.tf#L161) | Name of Dataplex Scan. | string
| โ | |
+| [project_id](variables.tf#L172) | The ID of the project where the Dataplex DataScan will be created. | string
| โ | |
+| [region](variables.tf#L177) | Region for the Dataplex DataScan. | string
| โ | |
| [data_profile_spec](variables.tf#L29) | DataProfileScan related setting. Variable descriptions are provided in https://cloud.google.com/dataplex/docs/reference/rest/v1/DataProfileSpec. | object({…})
| | null
|
| [data_quality_spec](variables.tf#L38) | DataQualityScan related setting. Variable descriptions are provided in https://cloud.google.com/dataplex/docs/reference/rest/v1/DataQualitySpec. | object({…})
| | null
|
| [data_quality_spec_file](variables.tf#L80) | Path to a YAML file containing DataQualityScan related setting. Input content can use either camelCase or snake_case. Variables description are provided in https://cloud.google.com/dataplex/docs/reference/rest/v1/DataQualitySpec. | object({…})
| | null
|
@@ -445,11 +445,11 @@ module "dataplex-datascan" {
| [iam](variables.tf#L107) | Dataplex DataScan IAM bindings in {ROLE => [MEMBERS]} format. | map(list(string))
| | {}
|
| [iam_additive](variables.tf#L114) | IAM additive bindings in {ROLE => [MEMBERS]} format. | map(list(string))
| | {}
|
| [iam_additive_members](variables.tf#L121) | IAM additive bindings in {MEMBERS => [ROLE]} format. This might break if members are dynamic values. | map(list(string))
| | {}
|
-| [iam_members](variables.tf#L127) | Individual additive IAM bindings, use this when iam_additive does not work due to dynamic resources. Keys are arbitrary and only used for the internal loop. | map(object({…}))
| | {}
|
-| [iam_policy](variables.tf#L137) | IAM authoritative policy in {ROLE => [MEMBERS]} format. Roles and members not explicitly listed will be cleared, use with extreme caution. | map(list(string))
| | null
|
-| [incremental_field](variables.tf#L143) | The unnested field (of type Date or Timestamp) that contains values which monotonically increase over time. If not specified, a data scan will run for all data in the table. | string
| | null
|
-| [labels](variables.tf#L149) | Resource labels. | map(string)
| | {}
|
-| [prefix](variables.tf#L161) | Optional prefix used to generate Dataplex DataScan ID. | string
| | null
|
+| [iam_members](variables.tf#L127) | Individual additive IAM bindings, use this when iam_additive does not work due to dynamic resources. Keys are arbitrary and only used for the internal loop. | map(object({…}))
| | {}
|
+| [iam_policy](variables.tf#L142) | IAM authoritative policy in {ROLE => [MEMBERS]} format. Roles and members not explicitly listed will be cleared, use with extreme caution. | map(list(string))
| | null
|
+| [incremental_field](variables.tf#L148) | The unnested field (of type Date or Timestamp) that contains values which monotonically increase over time. If not specified, a data scan will run for all data in the table. | string
| | null
|
+| [labels](variables.tf#L154) | Resource labels. | map(string)
| | {}
|
+| [prefix](variables.tf#L166) | Optional prefix used to generate Dataplex DataScan ID. | string
| | null
|
## Outputs
diff --git a/modules/dataplex-datascan/iam.tf b/modules/dataplex-datascan/iam.tf
index e1a7c057..2a6c4c78 100644
--- a/modules/dataplex-datascan/iam.tf
+++ b/modules/dataplex-datascan/iam.tf
@@ -76,6 +76,14 @@ resource "google_dataplex_datascan_iam_member" "members" {
data_scan_id = google_dataplex_datascan.datascan.data_scan_id
role = each.value.role
member = each.value.member
+ dynamic "condition" {
+ for_each = each.value.condition == null ? [] : [""]
+ content {
+ expression = each.value.condition.expression
+ title = each.value.condition.title
+ description = each.value.condition.description
+ }
+ }
}
resource "google_dataplex_datascan_iam_policy" "authoritative_for_resource" {
diff --git a/modules/dataplex-datascan/variables.tf b/modules/dataplex-datascan/variables.tf
index 47ca7332..f40ca4ec 100644
--- a/modules/dataplex-datascan/variables.tf
+++ b/modules/dataplex-datascan/variables.tf
@@ -129,6 +129,11 @@ variable "iam_members" {
type = map(object({
member = string
role = string
+ condition = optional(object({
+ expression = string
+ title = string
+ description = optional(string)
+ }))
}))
nullable = false
default = {}
diff --git a/modules/dataproc/README.md b/modules/dataproc/README.md
index 3e454e50..b9dfe3d0 100644
--- a/modules/dataproc/README.md
+++ b/modules/dataproc/README.md
@@ -165,17 +165,17 @@ module "processing-dp-cluster" {
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
-| [name](variables.tf#L222) | Cluster name. | string
| โ | |
-| [project_id](variables.tf#L237) | Project ID. | string
| โ | |
-| [region](variables.tf#L242) | Dataproc region. | string
| โ | |
+| [name](variables.tf#L227) | Cluster name. | string
| โ | |
+| [project_id](variables.tf#L242) | Project ID. | string
| โ | |
+| [region](variables.tf#L247) | Dataproc region. | string
| โ | |
| [dataproc_config](variables.tf#L17) | Dataproc cluster config. | object({…})
| | {}
|
| [group_iam](variables.tf#L185) | Authoritative IAM binding for organization groups, in {GROUP_EMAIL => [ROLES]} format. Group emails need to be static. Can be used in combination with the `iam` variable. | map(list(string))
| | {}
|
| [iam](variables.tf#L192) | IAM bindings in {ROLE => [MEMBERS]} format. | map(list(string))
| | {}
|
| [iam_additive](variables.tf#L199) | IAM additive bindings in {ROLE => [MEMBERS]} format. | map(list(string))
| | {}
|
-| [iam_members](variables.tf#L206) | Individual additive IAM bindings, use this when iam_additive does not work due to dynamic resources. Keys are arbitrary and only used for the internal loop. | map(object({…}))
| | {}
|
-| [labels](variables.tf#L216) | The resource labels for instance to use to annotate any related underlying resources, such as Compute Engine VMs. | map(string)
| | {}
|
-| [prefix](variables.tf#L227) | Optional prefix used to generate project id and name. | string
| | null
|
-| [service_account](variables.tf#L247) | Service account to set on the Dataproc cluster. | string
| | null
|
+| [iam_members](variables.tf#L206) | Individual additive IAM bindings, use this when iam_additive does not work due to dynamic resources. Keys are arbitrary and only used for the internal loop. | map(object({…}))
| | {}
|
+| [labels](variables.tf#L221) | The resource labels for instance to use to annotate any related underlying resources, such as Compute Engine VMs. | map(string)
| | {}
|
+| [prefix](variables.tf#L232) | Optional prefix used to generate project id and name. | string
| | null
|
+| [service_account](variables.tf#L252) | Service account to set on the Dataproc cluster. | string
| | null
|
## Outputs
diff --git a/modules/dataproc/iam.tf b/modules/dataproc/iam.tf
index 84d49f35..04756192 100644
--- a/modules/dataproc/iam.tf
+++ b/modules/dataproc/iam.tf
@@ -72,4 +72,12 @@ resource "google_dataproc_cluster_iam_member" "members" {
cluster = google_dataproc_cluster.cluster.name
role = each.value.role
member = each.value.member
+ dynamic "condition" {
+ for_each = each.value.condition == null ? [] : [""]
+ content {
+ expression = each.value.condition.expression
+ title = each.value.condition.title
+ description = each.value.condition.description
+ }
+ }
}
diff --git a/modules/dataproc/variables.tf b/modules/dataproc/variables.tf
index b28b9c27..37aad39f 100644
--- a/modules/dataproc/variables.tf
+++ b/modules/dataproc/variables.tf
@@ -208,6 +208,11 @@ variable "iam_members" {
type = map(object({
member = string
role = string
+ condition = optional(object({
+ expression = string
+ title = string
+ description = optional(string)
+ }))
}))
nullable = false
default = {}
diff --git a/modules/folder/README.md b/modules/folder/README.md
index b46c5e8b..03c2a755 100644
--- a/modules/folder/README.md
+++ b/modules/folder/README.md
@@ -325,17 +325,17 @@ module "folder" {
| [iam](variables.tf#L44) | IAM bindings in {ROLE => [MEMBERS]} format. | map(list(string))
| | {}
|
| [iam_additive](variables.tf#L51) | Non authoritative IAM bindings, in {ROLE => [MEMBERS]} format. | map(list(string))
| | {}
|
| [iam_additive_members](variables.tf#L58) | IAM additive bindings in {MEMBERS => [ROLE]} format. This might break if members are dynamic values. | map(list(string))
| | {}
|
-| [iam_members](variables.tf#L65) | Individual additive IAM bindings, use this when iam_additive does not work due to dynamic resources. Keys are arbitrary and only used for the internal loop. | map(object({…}))
| | {}
|
-| [iam_policy](variables.tf#L75) | IAM authoritative policy in {ROLE => [MEMBERS]} format. Roles and members not explicitly listed will be cleared, use with extreme caution. | map(list(string))
| | null
|
-| [id](variables.tf#L81) | Folder ID in case you use folder_create=false. | string
| | null
|
-| [logging_data_access](variables.tf#L87) | Control activation of data access logs. Format is service => { log type => [exempted members]}. The special 'allServices' key denotes configuration for all services. | map(map(list(string)))
| | {}
|
-| [logging_exclusions](variables.tf#L102) | Logging exclusions for this folder in the form {NAME -> FILTER}. | map(string)
| | {}
|
-| [logging_sinks](variables.tf#L109) | Logging sinks to create for the organization. | map(object({…}))
| | {}
|
-| [name](variables.tf#L139) | Folder name. | string
| | null
|
-| [org_policies](variables.tf#L145) | Organization policies applied to this folder keyed by policy name. | map(object({…}))
| | {}
|
-| [org_policies_data_path](variables.tf#L172) | Path containing org policies in YAML format. | string
| | null
|
-| [parent](variables.tf#L178) | Parent in folders/folder_id or organizations/org_id format. | string
| | null
|
-| [tag_bindings](variables.tf#L188) | Tag bindings for this folder, in key => tag value id format. | map(string)
| | null
|
+| [iam_members](variables.tf#L65) | Individual additive IAM bindings, use this when iam_additive does not work due to dynamic resources. Keys are arbitrary and only used for the internal loop. | map(object({…}))
| | {}
|
+| [iam_policy](variables.tf#L80) | IAM authoritative policy in {ROLE => [MEMBERS]} format. Roles and members not explicitly listed will be cleared, use with extreme caution. | map(list(string))
| | null
|
+| [id](variables.tf#L86) | Folder ID in case you use folder_create=false. | string
| | null
|
+| [logging_data_access](variables.tf#L92) | Control activation of data access logs. Format is service => { log type => [exempted members]}. The special 'allServices' key denotes configuration for all services. | map(map(list(string)))
| | {}
|
+| [logging_exclusions](variables.tf#L107) | Logging exclusions for this folder in the form {NAME -> FILTER}. | map(string)
| | {}
|
+| [logging_sinks](variables.tf#L114) | Logging sinks to create for the organization. | map(object({…}))
| | {}
|
+| [name](variables.tf#L144) | Folder name. | string
| | null
|
+| [org_policies](variables.tf#L150) | Organization policies applied to this folder keyed by policy name. | map(object({…}))
| | {}
|
+| [org_policies_data_path](variables.tf#L177) | Path containing org policies in YAML format. | string
| | null
|
+| [parent](variables.tf#L183) | Parent in folders/folder_id or organizations/org_id format. | string
| | null
|
+| [tag_bindings](variables.tf#L193) | Tag bindings for this folder, in key => tag value id format. | map(string)
| | null
|
## Outputs
diff --git a/modules/folder/iam.tf b/modules/folder/iam.tf
index 6b8fc1b1..cc457e28 100644
--- a/modules/folder/iam.tf
+++ b/modules/folder/iam.tf
@@ -69,6 +69,14 @@ resource "google_folder_iam_member" "members" {
folder = local.folder.name
role = each.value.role
member = each.value.member
+ dynamic "condition" {
+ for_each = each.value.condition == null ? [] : [""]
+ content {
+ expression = each.value.condition.expression
+ title = each.value.condition.title
+ description = each.value.condition.description
+ }
+ }
}
resource "google_folder_iam_policy" "authoritative" {
diff --git a/modules/folder/variables.tf b/modules/folder/variables.tf
index be7aad32..df057995 100644
--- a/modules/folder/variables.tf
+++ b/modules/folder/variables.tf
@@ -67,6 +67,11 @@ variable "iam_members" {
type = map(object({
member = string
role = string
+ condition = optional(object({
+ expression = string
+ title = string
+ description = optional(string)
+ }))
}))
nullable = false
default = {}
diff --git a/modules/iam-service-account/README.md b/modules/iam-service-account/README.md
index 061b651e..6c03b824 100644
--- a/modules/iam-service-account/README.md
+++ b/modules/iam-service-account/README.md
@@ -41,8 +41,8 @@ module "myproject-default-service-accounts" {
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
-| [name](variables.tf#L101) | Name of the service account to create. | string
| โ | |
-| [project_id](variables.tf#L116) | Project id where service account will be created. | string
| โ | |
+| [name](variables.tf#L106) | Name of the service account to create. | string
| โ | |
+| [project_id](variables.tf#L121) | Project id where service account will be created. | string
| โ | |
| [description](variables.tf#L17) | Optional description. | string
| | null
|
| [display_name](variables.tf#L23) | Display name of the service account to create. | string
| | "Terraform-managed."
|
| [generate_key](variables.tf#L29) | Generate a key for service account. | bool
| | false
|
@@ -50,14 +50,14 @@ module "myproject-default-service-accounts" {
| [iam_additive](variables.tf#L42) | IAM additive bindings on the service account in {ROLE => [MEMBERS]} format. | map(list(string))
| | {}
|
| [iam_billing_roles](variables.tf#L49) | Billing account roles granted to this service account, by billing account id. Non-authoritative. | map(list(string))
| | {}
|
| [iam_folder_roles](variables.tf#L56) | Folder roles granted to this service account, by folder id. Non-authoritative. | map(list(string))
| | {}
|
-| [iam_members](variables.tf#L63) | Individual additive IAM bindings, use this when iam_additive does not work due to dynamic resources. Keys are arbitrary and only used for the internal loop. | map(object({…}))
| | {}
|
-| [iam_organization_roles](variables.tf#L73) | Organization roles granted to this service account, by organization id. Non-authoritative. | map(list(string))
| | {}
|
-| [iam_project_roles](variables.tf#L80) | Project roles granted to this service account, by project id. | map(list(string))
| | {}
|
-| [iam_sa_roles](variables.tf#L87) | Service account roles granted to this service account, by service account name. | map(list(string))
| | {}
|
-| [iam_storage_roles](variables.tf#L94) | Storage roles granted to this service account, by bucket name. | map(list(string))
| | {}
|
-| [prefix](variables.tf#L106) | Prefix applied to service account names. | string
| | null
|
-| [public_keys_directory](variables.tf#L121) | Path to public keys data files to upload to the service account (should have `.pem` extension). | string
| | ""
|
-| [service_account_create](variables.tf#L127) | Create service account. When set to false, uses a data source to reference an existing service account. | bool
| | true
|
+| [iam_members](variables.tf#L63) | Individual additive IAM bindings, use this when iam_additive does not work due to dynamic resources. Keys are arbitrary and only used for the internal loop. | map(object({…}))
| | {}
|
+| [iam_organization_roles](variables.tf#L78) | Organization roles granted to this service account, by organization id. Non-authoritative. | map(list(string))
| | {}
|
+| [iam_project_roles](variables.tf#L85) | Project roles granted to this service account, by project id. | map(list(string))
| | {}
|
+| [iam_sa_roles](variables.tf#L92) | Service account roles granted to this service account, by service account name. | map(list(string))
| | {}
|
+| [iam_storage_roles](variables.tf#L99) | Storage roles granted to this service account, by bucket name. | map(list(string))
| | {}
|
+| [prefix](variables.tf#L111) | Prefix applied to service account names. | string
| | null
|
+| [public_keys_directory](variables.tf#L126) | Path to public keys data files to upload to the service account (should have `.pem` extension). | string
| | ""
|
+| [service_account_create](variables.tf#L132) | Create service account. When set to false, uses a data source to reference an existing service account. | bool
| | true
|
## Outputs
diff --git a/modules/iam-service-account/iam.tf b/modules/iam-service-account/iam.tf
index ae388c8e..a92d5e2a 100644
--- a/modules/iam-service-account/iam.tf
+++ b/modules/iam-service-account/iam.tf
@@ -139,6 +139,14 @@ resource "google_service_account_iam_member" "members" {
service_account_id = each.value.entity
role = each.value.role
member = each.value.member
+ dynamic "condition" {
+ for_each = each.value.condition == null ? [] : [""]
+ content {
+ expression = each.value.condition.expression
+ title = each.value.condition.title
+ description = each.value.condition.description
+ }
+ }
}
resource "google_storage_bucket_iam_member" "bucket-roles" {
diff --git a/modules/iam-service-account/variables.tf b/modules/iam-service-account/variables.tf
index 3594fbbc..87c350b6 100644
--- a/modules/iam-service-account/variables.tf
+++ b/modules/iam-service-account/variables.tf
@@ -65,6 +65,11 @@ variable "iam_members" {
type = map(object({
member = string
role = string
+ condition = optional(object({
+ expression = string
+ title = string
+ description = optional(string)
+ }))
}))
nullable = false
default = {}
diff --git a/modules/kms/README.md b/modules/kms/README.md
index 7b8eb487..5bd82d5f 100644
--- a/modules/kms/README.md
+++ b/modules/kms/README.md
@@ -101,19 +101,19 @@ module "kms" {
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
-| [keyring](variables.tf#L91) | Keyring attributes. | object({…})
| โ | |
-| [project_id](variables.tf#L114) | Project id where the keyring will be created. | string
| โ | |
+| [keyring](variables.tf#L101) | Keyring attributes. | object({…})
| โ | |
+| [project_id](variables.tf#L124) | Project id where the keyring will be created. | string
| โ | |
| [iam](variables.tf#L17) | Keyring IAM bindings in {ROLE => [MEMBERS]} format. | map(list(string))
| | {}
|
| [iam_additive](variables.tf#L23) | Keyring IAM additive bindings in {ROLE => [MEMBERS]} format. | map(list(string))
| | {}
|
-| [iam_members](variables.tf#L29) | Individual additive IAM bindings, use this when iam_additive does not work due to dynamic resources. Keys are arbitrary and only used for the internal loop. | map(object({…}))
| | {}
|
-| [key_iam](variables.tf#L39) | Key IAM bindings in {KEY => {ROLE => [MEMBERS]}} format. | map(map(list(string)))
| | {}
|
-| [key_iam_additive](variables.tf#L45) | Key IAM additive bindings in {KEY => {ROLE => [MEMBERS]}} format. | map(map(list(string)))
| | {}
|
-| [key_iam_members](variables.tf#L51) | Individual additive IAM bindings, use this when iam_additive does not work due to dynamic resources. Keys are arbitrary and only used for the internal loop. | map(object({…}))
| | {}
|
-| [key_purpose](variables.tf#L62) | Per-key purpose, if not set defaults will be used. If purpose is not `ENCRYPT_DECRYPT` (the default), `version_template.algorithm` is required. | map(object({…}))
| | {}
|
-| [key_purpose_defaults](variables.tf#L74) | Defaults used for key purpose when not defined at the key level. If purpose is not `ENCRYPT_DECRYPT` (the default), `version_template.algorithm` is required. | object({…})
| | {…}
|
-| [keyring_create](variables.tf#L99) | Set to false to manage keys and IAM bindings in an existing keyring. | bool
| | true
|
-| [keys](variables.tf#L105) | Key names and base attributes. Set attributes to null if not needed. | map(object({…}))
| | {}
|
-| [tag_bindings](variables.tf#L119) | Tag bindings for this keyring, in key => tag value id format. | map(string)
| | null
|
+| [iam_members](variables.tf#L29) | Individual additive IAM bindings, use this when iam_additive does not work due to dynamic resources. Keys are arbitrary and only used for the internal loop. | map(object({…}))
| | {}
|
+| [key_iam](variables.tf#L44) | Key IAM bindings in {KEY => {ROLE => [MEMBERS]}} format. | map(map(list(string)))
| | {}
|
+| [key_iam_additive](variables.tf#L50) | Key IAM additive bindings in {KEY => {ROLE => [MEMBERS]}} format. | map(map(list(string)))
| | {}
|
+| [key_iam_members](variables.tf#L56) | Individual additive IAM bindings, use this when iam_additive does not work due to dynamic resources. Keys are arbitrary and only used for the internal loop. | map(object({…}))
| | {}
|
+| [key_purpose](variables.tf#L72) | Per-key purpose, if not set defaults will be used. If purpose is not `ENCRYPT_DECRYPT` (the default), `version_template.algorithm` is required. | map(object({…}))
| | {}
|
+| [key_purpose_defaults](variables.tf#L84) | Defaults used for key purpose when not defined at the key level. If purpose is not `ENCRYPT_DECRYPT` (the default), `version_template.algorithm` is required. | object({…})
| | {…}
|
+| [keyring_create](variables.tf#L109) | Set to false to manage keys and IAM bindings in an existing keyring. | bool
| | true
|
+| [keys](variables.tf#L115) | Key names and base attributes. Set attributes to null if not needed. | map(object({…}))
| | {}
|
+| [tag_bindings](variables.tf#L129) | Tag bindings for this keyring, in key => tag value id format. | map(string)
| | null
|
## Outputs
diff --git a/modules/kms/iam.tf b/modules/kms/iam.tf
index 3da20d08..9289a4e8 100644
--- a/modules/kms/iam.tf
+++ b/modules/kms/iam.tf
@@ -67,6 +67,14 @@ resource "google_kms_key_ring_iam_member" "members" {
key_ring_id = local.keyring.id
role = each.value.role
member = each.value.member
+ dynamic "condition" {
+ for_each = each.value.condition == null ? [] : [""]
+ content {
+ expression = each.value.condition.expression
+ title = each.value.condition.title
+ description = each.value.condition.description
+ }
+ }
}
resource "google_kms_crypto_key_iam_binding" "default" {
@@ -94,4 +102,12 @@ resource "google_kms_crypto_key_iam_member" "members" {
crypto_key_id = google_kms_crypto_key.default[each.value.key].id
role = each.value.role
member = each.value.member
+ dynamic "condition" {
+ for_each = each.value.condition == null ? [] : [""]
+ content {
+ expression = each.value.condition.expression
+ title = each.value.condition.title
+ description = each.value.condition.description
+ }
+ }
}
diff --git a/modules/kms/variables.tf b/modules/kms/variables.tf
index 01e96c04..c34aaaac 100644
--- a/modules/kms/variables.tf
+++ b/modules/kms/variables.tf
@@ -31,6 +31,11 @@ variable "iam_members" {
type = map(object({
member = string
role = string
+ condition = optional(object({
+ expression = string
+ title = string
+ description = optional(string)
+ }))
}))
nullable = false
default = {}
@@ -54,6 +59,11 @@ variable "key_iam_members" {
key = string
member = string
role = string
+ condition = optional(object({
+ expression = string
+ title = string
+ description = optional(string)
+ }))
}))
nullable = false
default = {}
diff --git a/modules/net-vpc/README.md b/modules/net-vpc/README.md
index 8106e1d9..0ccfeb86 100644
--- a/modules/net-vpc/README.md
+++ b/modules/net-vpc/README.md
@@ -541,11 +541,11 @@ module "vpc" {
| [shared_vpc_service_projects](variables.tf#L161) | Shared VPC service projects to register with this host. | list(string)
| | []
|
| [subnet_iam](variables.tf#L167) | Subnet IAM bindings in {REGION/NAME => {ROLE => [MEMBERS]} format. | map(map(list(string)))
| | {}
|
| [subnet_iam_additive](variables.tf#L173) | Subnet IAM additive bindings in {REGION/NAME => {ROLE => [MEMBERS]}} format. | map(map(list(string)))
| | {}
|
-| [subnet_iam_members](variables.tf#L180) | Individual additive IAM bindings, use this when iam_additive does not work due to dynamic resources. Keys are arbitrary and only used for the internal loop. | map(object({…}))
| | {}
|
-| [subnets](variables.tf#L191) | Subnet configuration. | list(object({…}))
| | []
|
-| [subnets_proxy_only](variables.tf#L217) | List of proxy-only subnets for Regional HTTPS or Internal HTTPS load balancers. Note: Only one proxy-only subnet for each VPC network in each region can be active. | list(object({…}))
| | []
|
-| [subnets_psc](variables.tf#L229) | List of subnets for Private Service Connect service producers. | list(object({…}))
| | []
|
-| [vpc_create](variables.tf#L240) | Create VPC. When set to false, uses a data source to reference existing VPC. | bool
| | true
|
+| [subnet_iam_members](variables.tf#L180) | Individual additive IAM bindings, use this when iam_additive does not work due to dynamic resources. Keys are arbitrary and only used for the internal loop. | map(object({…}))
| | {}
|
+| [subnets](variables.tf#L196) | Subnet configuration. | list(object({…}))
| | []
|
+| [subnets_proxy_only](variables.tf#L222) | List of proxy-only subnets for Regional HTTPS or Internal HTTPS load balancers. Note: Only one proxy-only subnet for each VPC network in each region can be active. | list(object({…}))
| | []
|
+| [subnets_psc](variables.tf#L234) | List of subnets for Private Service Connect service producers. | list(object({…}))
| | []
|
+| [vpc_create](variables.tf#L245) | Create VPC. When set to false, uses a data source to reference existing VPC. | bool
| | true
|
## Outputs
diff --git a/modules/net-vpc/subnets.tf b/modules/net-vpc/subnets.tf
index 16f6398c..b6eab182 100644
--- a/modules/net-vpc/subnets.tf
+++ b/modules/net-vpc/subnets.tf
@@ -197,4 +197,12 @@ resource "google_compute_subnetwork_iam_member" "members" {
region = google_compute_subnetwork.subnetwork[each.value.subnet].region
role = each.value.role
member = each.value.member
+ dynamic "condition" {
+ for_each = each.value.condition == null ? [] : [""]
+ content {
+ expression = each.value.condition.expression
+ title = each.value.condition.title
+ description = each.value.condition.description
+ }
+ }
}
diff --git a/modules/net-vpc/variables.tf b/modules/net-vpc/variables.tf
index 4e114dfe..c49455fa 100644
--- a/modules/net-vpc/variables.tf
+++ b/modules/net-vpc/variables.tf
@@ -183,6 +183,11 @@ variable "subnet_iam_members" {
member = string
role = string
subnet = string
+ condition = optional(object({
+ expression = string
+ title = string
+ description = optional(string)
+ }))
}))
nullable = false
default = {}
diff --git a/modules/organization/README.md b/modules/organization/README.md
index d232dfd4..4984d2a8 100644
--- a/modules/organization/README.md
+++ b/modules/organization/README.md
@@ -475,7 +475,7 @@ module "org" {
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
-| [organization_id](variables.tf#L209) | Organization id in organizations/nnnnnn format. | string
| โ | |
+| [organization_id](variables.tf#L214) | Organization id in organizations/nnnnnn format. | string
| โ | |
| [contacts](variables.tf#L17) | List of essential contacts for this resource. Must be in the form EMAIL -> [NOTIFICATION_TYPES]. Valid notification types are ALL, SUSPENSION, SECURITY, TECHNICAL, BILLING, LEGAL, PRODUCT_UPDATES. | map(list(string))
| | {}
|
| [custom_roles](variables.tf#L24) | Map of role name => list of permissions to create in this project. | map(list(string))
| | {}
|
| [firewall_policy_associations](variables.tf#L31) | Hierarchical firewall policies to associate to this folder, in association name => policy id format. | map(string)
| | {}
|
@@ -483,18 +483,18 @@ module "org" {
| [iam](variables.tf#L45) | IAM bindings, in {ROLE => [MEMBERS]} format. | map(list(string))
| | {}
|
| [iam_additive](variables.tf#L52) | Non authoritative IAM bindings, in {ROLE => [MEMBERS]} format. | map(list(string))
| | {}
|
| [iam_additive_members](variables.tf#L59) | IAM additive bindings in {MEMBERS => [ROLE]} format. This might break if members are dynamic values. | map(list(string))
| | {}
|
-| [iam_members](variables.tf#L66) | Individual additive IAM bindings, use this when iam_additive does not work due to dynamic resources. Keys are arbitrary and only used for the internal loop. | map(object({…}))
| | {}
|
-| [iam_policy](variables.tf#L76) | IAM authoritative policy in {ROLE => [MEMBERS]} format. Roles and members not explicitly listed will be cleared, use with extreme caution. | map(list(string))
| | null
|
-| [logging_data_access](variables.tf#L82) | Control activation of data access logs. Format is service => { log type => [exempted members]}. The special 'allServices' key denotes configuration for all services. | map(map(list(string)))
| | {}
|
-| [logging_exclusions](variables.tf#L97) | Logging exclusions for this organization in the form {NAME -> FILTER}. | map(string)
| | {}
|
-| [logging_sinks](variables.tf#L104) | Logging sinks to create for the organization. | map(object({…}))
| | {}
|
-| [network_tags](variables.tf#L134) | Network tags by key name. If `id` is provided, key creation is skipped. The `iam` attribute behaves like the similarly named one at module level. | map(object({…}))
| | {}
|
-| [org_policies](variables.tf#L156) | Organization policies applied to this organization keyed by policy name. | map(object({…}))
| | {}
|
-| [org_policies_data_path](variables.tf#L183) | Path containing org policies in YAML format. | string
| | null
|
-| [org_policy_custom_constraints](variables.tf#L189) | Organization policy custom constraints keyed by constraint name. | map(object({…}))
| | {}
|
-| [org_policy_custom_constraints_data_path](variables.tf#L203) | Path containing org policy custom constraints in YAML format. | string
| | null
|
-| [tag_bindings](variables.tf#L218) | Tag bindings for this organization, in key => tag value id format. | map(string)
| | null
|
-| [tags](variables.tf#L224) | Tags by key name. If `id` is provided, key or value creation is skipped. The `iam` attribute behaves like the similarly named one at module level. | map(object({…}))
| | {}
|
+| [iam_members](variables.tf#L66) | Individual additive IAM bindings, use this when iam_additive does not work due to dynamic resources. Keys are arbitrary and only used for the internal loop. | map(object({…}))
| | {}
|
+| [iam_policy](variables.tf#L81) | IAM authoritative policy in {ROLE => [MEMBERS]} format. Roles and members not explicitly listed will be cleared, use with extreme caution. | map(list(string))
| | null
|
+| [logging_data_access](variables.tf#L87) | Control activation of data access logs. Format is service => { log type => [exempted members]}. The special 'allServices' key denotes configuration for all services. | map(map(list(string)))
| | {}
|
+| [logging_exclusions](variables.tf#L102) | Logging exclusions for this organization in the form {NAME -> FILTER}. | map(string)
| | {}
|
+| [logging_sinks](variables.tf#L109) | Logging sinks to create for the organization. | map(object({…}))
| | {}
|
+| [network_tags](variables.tf#L139) | Network tags by key name. If `id` is provided, key creation is skipped. The `iam` attribute behaves like the similarly named one at module level. | map(object({…}))
| | {}
|
+| [org_policies](variables.tf#L161) | Organization policies applied to this organization keyed by policy name. | map(object({…}))
| | {}
|
+| [org_policies_data_path](variables.tf#L188) | Path containing org policies in YAML format. | string
| | null
|
+| [org_policy_custom_constraints](variables.tf#L194) | Organization policy custom constraints keyed by constraint name. | map(object({…}))
| | {}
|
+| [org_policy_custom_constraints_data_path](variables.tf#L208) | Path containing org policy custom constraints in YAML format. | string
| | null
|
+| [tag_bindings](variables.tf#L223) | Tag bindings for this organization, in key => tag value id format. | map(string)
| | null
|
+| [tags](variables.tf#L229) | Tags by key name. If `id` is provided, key or value creation is skipped. The `iam` attribute behaves like the similarly named one at module level. | map(object({…}))
| | {}
|
## Outputs
diff --git a/modules/organization/iam.tf b/modules/organization/iam.tf
index f5d3b2d7..e5ba3771 100644
--- a/modules/organization/iam.tf
+++ b/modules/organization/iam.tf
@@ -78,6 +78,14 @@ resource "google_organization_iam_member" "members" {
org_id = local.organization_id_numeric
role = each.value.role
member = each.value.member
+ dynamic "condition" {
+ for_each = each.value.condition == null ? [] : [""]
+ content {
+ expression = each.value.condition.expression
+ title = each.value.condition.title
+ description = each.value.condition.description
+ }
+ }
}
resource "google_organization_iam_policy" "authoritative" {
diff --git a/modules/organization/variables.tf b/modules/organization/variables.tf
index 65f0672b..2426f8e9 100644
--- a/modules/organization/variables.tf
+++ b/modules/organization/variables.tf
@@ -68,6 +68,11 @@ variable "iam_members" {
type = map(object({
member = string
role = string
+ condition = optional(object({
+ expression = string
+ title = string
+ description = optional(string)
+ }))
}))
nullable = false
default = {}
diff --git a/modules/project/README.md b/modules/project/README.md
index 8a867b51..8ec483fb 100644
--- a/modules/project/README.md
+++ b/modules/project/README.md
@@ -154,7 +154,7 @@ module "project" {
#### Additive IAM by Binding
-When the above approaches to additive IAM are unworkable due to dynamically generated principals, the `iam_members` variable allows specifying individual role/principal pairs using arbitrary keys:
+When the above approaches to additive IAM are unworkable due to dynamically generated principals, the `iam_members` variable allows specifying individual role/principal pairs using arbitrary keys. This IAM variable also supports conditions.
```hcl
module "project" {
@@ -173,10 +173,23 @@ module "project" {
member = "user:two@example.org"
role = "roles/compute.admin"
}
+ one-delegated-grant = {
+ member = "user:one@example.org"
+ role = "roles/resourcemanager.projectIamAdmin"
+ condition = {
+ title = "delegated_network_user_one"
+ expression = <<-END
+ api.getAttribute(
+ 'iam.googleapis.com/modifiedGrantsByRole', []
+ ).hasOnly([
+ 'roles/compute.networkAdmin'
+ ])
+ END
+ }
+ }
}
-
}
-# tftest modules=1 resources=4 inventory=iam-members.yaml
+# tftest modules=1 resources=5 inventory=iam-members.yaml
```
### Service Identities and Authoritative IAM
@@ -658,7 +671,7 @@ output "compute_robot" {
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
-| [name](variables.tf#L171) | Project name and id suffix. | string
| โ | |
+| [name](variables.tf#L176) | Project name and id suffix. | string
| โ | |
| [auto_create_network](variables.tf#L17) | Whether to create the default network for the project. | bool
| | false
|
| [billing_account](variables.tf#L23) | Billing account id. | string
| | null
|
| [contacts](variables.tf#L29) | List of essential contacts for this resource. Must be in the form EMAIL -> [NOTIFICATION_TYPES]. Valid notification types are ALL, SUSPENSION, SECURITY, TECHNICAL, BILLING, LEGAL, PRODUCT_UPDATES. | map(list(string))
| | {}
|
@@ -669,31 +682,31 @@ output "compute_robot" {
| [iam](variables.tf#L62) | IAM bindings in {ROLE => [MEMBERS]} format. | map(list(string))
| | {}
|
| [iam_additive](variables.tf#L69) | IAM additive bindings in {ROLE => [MEMBERS]} format. | map(list(string))
| | {}
|
| [iam_additive_members](variables.tf#L76) | IAM additive bindings in {MEMBERS => [ROLE]} format. This might break if members are dynamic values. | map(list(string))
| | {}
|
-| [iam_members](variables.tf#L82) | Individual additive IAM bindings, use this when iam_additive does not work due to dynamic resources. Keys are arbitrary and only used for the internal loop. | map(object({…}))
| | {}
|
-| [iam_policy](variables.tf#L92) | IAM authoritative policy in {ROLE => [MEMBERS]} format. Roles and members not explicitly listed will be cleared, use with extreme caution. | map(list(string))
| | null
|
-| [labels](variables.tf#L98) | Resource labels. | map(string)
| | {}
|
-| [lien_reason](variables.tf#L105) | If non-empty, creates a project lien with this description. | string
| | ""
|
-| [logging_data_access](variables.tf#L111) | Control activation of data access logs. Format is service => { log type => [exempted members]}. The special 'allServices' key denotes configuration for all services. | map(map(list(string)))
| | {}
|
-| [logging_exclusions](variables.tf#L126) | Logging exclusions for this project in the form {NAME -> FILTER}. | map(string)
| | {}
|
-| [logging_sinks](variables.tf#L133) | Logging sinks to create for this project. | map(object({…}))
| | {}
|
-| [metric_scopes](variables.tf#L164) | List of projects that will act as metric scopes for this project. | list(string)
| | []
|
-| [org_policies](variables.tf#L176) | Organization policies applied to this project keyed by policy name. | map(object({…}))
| | {}
|
-| [org_policies_data_path](variables.tf#L203) | Path containing org policies in YAML format. | string
| | null
|
-| [oslogin](variables.tf#L209) | Enable OS Login. | bool
| | false
|
-| [oslogin_admins](variables.tf#L215) | List of IAM-style identities that will be granted roles necessary for OS Login administrators. | list(string)
| | []
|
-| [oslogin_users](variables.tf#L223) | List of IAM-style identities that will be granted roles necessary for OS Login users. | list(string)
| | []
|
-| [parent](variables.tf#L230) | Parent folder or organization in 'folders/folder_id' or 'organizations/org_id' format. | string
| | null
|
-| [prefix](variables.tf#L240) | Optional prefix used to generate project id and name. | string
| | null
|
-| [project_create](variables.tf#L250) | Create project. When set to false, uses a data source to reference existing project. | bool
| | true
|
-| [service_config](variables.tf#L256) | Configure service API activation. | object({…})
| | {…}
|
-| [service_encryption_key_ids](variables.tf#L268) | Cloud KMS encryption key in {SERVICE => [KEY_URL]} format. | map(list(string))
| | {}
|
-| [service_perimeter_bridges](variables.tf#L275) | Name of VPC-SC Bridge perimeters to add project into. See comment in the variables file for format. | list(string)
| | null
|
-| [service_perimeter_standard](variables.tf#L282) | Name of VPC-SC Standard perimeter to add project into. See comment in the variables file for format. | string
| | null
|
-| [services](variables.tf#L288) | Service APIs to enable. | list(string)
| | []
|
-| [shared_vpc_host_config](variables.tf#L294) | Configures this project as a Shared VPC host project (mutually exclusive with shared_vpc_service_project). | object({…})
| | null
|
-| [shared_vpc_service_config](variables.tf#L303) | Configures this project as a Shared VPC service project (mutually exclusive with shared_vpc_host_config). | object({…})
| | {…}
|
-| [skip_delete](variables.tf#L325) | Allows the underlying resources to be destroyed without destroying the project itself. | bool
| | false
|
-| [tag_bindings](variables.tf#L331) | Tag bindings for this project, in key => tag value id format. | map(string)
| | null
|
+| [iam_members](variables.tf#L82) | Individual additive IAM bindings, use this when iam_additive does not work due to dynamic resources. Keys are arbitrary and only used for the internal loop. | map(object({…}))
| | {}
|
+| [iam_policy](variables.tf#L97) | IAM authoritative policy in {ROLE => [MEMBERS]} format. Roles and members not explicitly listed will be cleared, use with extreme caution. | map(list(string))
| | null
|
+| [labels](variables.tf#L103) | Resource labels. | map(string)
| | {}
|
+| [lien_reason](variables.tf#L110) | If non-empty, creates a project lien with this description. | string
| | ""
|
+| [logging_data_access](variables.tf#L116) | Control activation of data access logs. Format is service => { log type => [exempted members]}. The special 'allServices' key denotes configuration for all services. | map(map(list(string)))
| | {}
|
+| [logging_exclusions](variables.tf#L131) | Logging exclusions for this project in the form {NAME -> FILTER}. | map(string)
| | {}
|
+| [logging_sinks](variables.tf#L138) | Logging sinks to create for this project. | map(object({…}))
| | {}
|
+| [metric_scopes](variables.tf#L169) | List of projects that will act as metric scopes for this project. | list(string)
| | []
|
+| [org_policies](variables.tf#L181) | Organization policies applied to this project keyed by policy name. | map(object({…}))
| | {}
|
+| [org_policies_data_path](variables.tf#L208) | Path containing org policies in YAML format. | string
| | null
|
+| [oslogin](variables.tf#L214) | Enable OS Login. | bool
| | false
|
+| [oslogin_admins](variables.tf#L220) | List of IAM-style identities that will be granted roles necessary for OS Login administrators. | list(string)
| | []
|
+| [oslogin_users](variables.tf#L228) | List of IAM-style identities that will be granted roles necessary for OS Login users. | list(string)
| | []
|
+| [parent](variables.tf#L235) | Parent folder or organization in 'folders/folder_id' or 'organizations/org_id' format. | string
| | null
|
+| [prefix](variables.tf#L245) | Optional prefix used to generate project id and name. | string
| | null
|
+| [project_create](variables.tf#L255) | Create project. When set to false, uses a data source to reference existing project. | bool
| | true
|
+| [service_config](variables.tf#L261) | Configure service API activation. | object({…})
| | {…}
|
+| [service_encryption_key_ids](variables.tf#L273) | Cloud KMS encryption key in {SERVICE => [KEY_URL]} format. | map(list(string))
| | {}
|
+| [service_perimeter_bridges](variables.tf#L280) | Name of VPC-SC Bridge perimeters to add project into. See comment in the variables file for format. | list(string)
| | null
|
+| [service_perimeter_standard](variables.tf#L287) | Name of VPC-SC Standard perimeter to add project into. See comment in the variables file for format. | string
| | null
|
+| [services](variables.tf#L293) | Service APIs to enable. | list(string)
| | []
|
+| [shared_vpc_host_config](variables.tf#L299) | Configures this project as a Shared VPC host project (mutually exclusive with shared_vpc_service_project). | object({…})
| | null
|
+| [shared_vpc_service_config](variables.tf#L308) | Configures this project as a Shared VPC service project (mutually exclusive with shared_vpc_host_config). | object({…})
| | {…}
|
+| [skip_delete](variables.tf#L330) | Allows the underlying resources to be destroyed without destroying the project itself. | bool
| | false
|
+| [tag_bindings](variables.tf#L336) | Tag bindings for this project, in key => tag value id format. | map(string)
| | null
|
## Outputs
diff --git a/modules/project/iam.tf b/modules/project/iam.tf
index 7b67d895..0918b552 100644
--- a/modules/project/iam.tf
+++ b/modules/project/iam.tf
@@ -102,6 +102,14 @@ resource "google_project_iam_member" "members" {
project = local.project.project_id
role = each.value.role
member = each.value.member
+ dynamic "condition" {
+ for_each = each.value.condition == null ? [] : [""]
+ content {
+ expression = each.value.condition.expression
+ title = each.value.condition.title
+ description = each.value.condition.description
+ }
+ }
depends_on = [
google_project_service.project_services,
google_project_iam_custom_role.roles
diff --git a/modules/project/variables.tf b/modules/project/variables.tf
index 4ab51062..18f55886 100644
--- a/modules/project/variables.tf
+++ b/modules/project/variables.tf
@@ -84,6 +84,11 @@ variable "iam_members" {
type = map(object({
member = string
role = string
+ condition = optional(object({
+ expression = string
+ title = string
+ description = optional(string)
+ }))
}))
nullable = false
default = {}
diff --git a/modules/projects-data-source/README.md b/modules/projects-data-source/README.md
index 25402a04..93dd67f4 100644
--- a/modules/projects-data-source/README.md
+++ b/modules/projects-data-source/README.md
@@ -80,7 +80,7 @@ output "filtered-projects" {
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
| [parent](variables.tf#L55) | Parent folder or organization in 'folders/folder_id' or 'organizations/org_id' format. | string
| โ | |
-| [ignore_folders](variables.tf#L17) | A list of folder IDs or numbers to be excluded from the output, all the subfolders and projects are exluded from the output regardless of the include_projects variable. | list(string)
| | []
|
+| [ignore_folders](variables.tf#L17) | A list of folder IDs or numbers to be excluded from the output, all the subfolders and projects are excluded from the output regardless of the include_projects variable. | list(string)
| | []
|
| [ignore_projects](variables.tf#L28) | A list of project IDs, numbers or prefixes to exclude matching projects from the module output. | list(string)
| | []
|
| [include_projects](variables.tf#L41) | A list of project IDs/numbers to include to the output if some of them are excluded by `ignore_projects` wildcard entries. | list(string)
| | []
|
| [query](variables.tf#L64) | A string query as defined in the [Query Syntax](https://cloud.google.com/asset-inventory/docs/query-syntax). | string
| | "state:ACTIVE"
|
diff --git a/modules/projects-data-source/variables.tf b/modules/projects-data-source/variables.tf
index c774784c..888cab21 100644
--- a/modules/projects-data-source/variables.tf
+++ b/modules/projects-data-source/variables.tf
@@ -15,7 +15,7 @@
*/
variable "ignore_folders" {
- description = "A list of folder IDs or numbers to be excluded from the output, all the subfolders and projects are exluded from the output regardless of the include_projects variable."
+ description = "A list of folder IDs or numbers to be excluded from the output, all the subfolders and projects are excluded from the output regardless of the include_projects variable."
type = list(string)
default = []
# example exlusing a folder
diff --git a/modules/source-repository/README.md b/modules/source-repository/README.md
index 4cfae8ea..01fff2d8 100644
--- a/modules/source-repository/README.md
+++ b/modules/source-repository/README.md
@@ -75,14 +75,14 @@ module "repo" {
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
-| [name](variables.tf#L54) | Repository name. | string
| โ | |
-| [project_id](variables.tf#L59) | Project used for resources. | string
| โ | |
+| [name](variables.tf#L59) | Repository name. | string
| โ | |
+| [project_id](variables.tf#L64) | Project used for resources. | string
| โ | |
| [group_iam](variables.tf#L17) | Authoritative IAM binding for organization groups, in {GROUP_EMAIL => [ROLES]} format. Group emails need to be static. Can be used in combination with the `iam` variable. | map(list(string))
| | {}
|
| [iam](variables.tf#L24) | IAM bindings in {ROLE => [MEMBERS]} format. | map(list(string))
| | {}
|
| [iam_additive](variables.tf#L31) | IAM additive bindings in {ROLE => [MEMBERS]} format. | map(list(string))
| | {}
|
| [iam_additive_members](variables.tf#L38) | IAM additive bindings in {MEMBERS => [ROLE]} format. This might break if members are dynamic values. | map(list(string))
| | {}
|
-| [iam_members](variables.tf#L44) | Individual additive IAM bindings, use this when iam_additive does not work due to dynamic resources. Keys are arbitrary and only used for the internal loop. | map(object({…}))
| | {}
|
-| [triggers](variables.tf#L64) | Cloud Build triggers. | map(object({…}))
| | {}
|
+| [iam_members](variables.tf#L44) | Individual additive IAM bindings, use this when iam_additive does not work due to dynamic resources. Keys are arbitrary and only used for the internal loop. | map(object({…}))
| | {}
|
+| [triggers](variables.tf#L69) | Cloud Build triggers. | map(object({…}))
| | {}
|
## Outputs
diff --git a/modules/source-repository/iam.tf b/modules/source-repository/iam.tf
index f1ec3ebc..75d00fd1 100644
--- a/modules/source-repository/iam.tf
+++ b/modules/source-repository/iam.tf
@@ -72,4 +72,12 @@ resource "google_sourcerepo_repository_iam_member" "members" {
repository = google_sourcerepo_repository.default.name
role = each.value.role
member = each.value.member
+ dynamic "condition" {
+ for_each = each.value.condition == null ? [] : [""]
+ content {
+ expression = each.value.condition.expression
+ title = each.value.condition.title
+ description = each.value.condition.description
+ }
+ }
}
diff --git a/modules/source-repository/variables.tf b/modules/source-repository/variables.tf
index 74e44aa4..61975fb9 100644
--- a/modules/source-repository/variables.tf
+++ b/modules/source-repository/variables.tf
@@ -46,6 +46,11 @@ variable "iam_members" {
type = map(object({
member = string
role = string
+ condition = optional(object({
+ expression = string
+ title = string
+ description = optional(string)
+ }))
}))
nullable = false
default = {}
diff --git a/tests/modules/project/examples/iam-members.yaml b/tests/modules/project/examples/iam-members.yaml
index b80fc2bf..41f48a56 100644
--- a/tests/modules/project/examples/iam-members.yaml
+++ b/tests/modules/project/examples/iam-members.yaml
@@ -23,6 +23,15 @@ values:
project_id: project-example
skip_delete: false
timeouts: null
+ module.project.google_project_iam_member.members["one-delegated-grant"]:
+ condition:
+ - description: null
+ expression: "api.getAttribute(\n 'iam.googleapis.com/modifiedGrantsByRole',\
+ \ []\n).hasOnly([\n 'roles/compute.networkAdmin'\n])\n"
+ title: delegated_network_user_one
+ member: user:one@example.org
+ project: project-example
+ role: roles/resourcemanager.projectIamAdmin
module.project.google_project_iam_member.members["one-owner"]:
condition: []
member: user:one@example.org
@@ -41,8 +50,9 @@ values:
counts:
google_project: 1
- google_project_iam_member: 3
+ google_project_iam_member: 4
modules: 1
- resources: 4
+ resources: 5
outputs: {}
+