From ec3b705f53152c9f5f5267e3051c2111ad5f811a Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Fri, 8 Sep 2023 08:56:31 +0200 Subject: [PATCH] Change type of `iam_bindings` variable to allow multiple conditional bindings (#1658) * modules * fast * dns readme --- fast/stages/0-bootstrap/organization.tf | 3 +- modules/__docs/20230816-iam-refactor.md | 8 ++- modules/data-catalog-policy-tag/README.md | 14 +++--- modules/data-catalog-policy-tag/iam.tf | 2 +- modules/data-catalog-policy-tag/variables.tf | 3 +- modules/dataplex-datascan/README.md | 16 +++--- modules/dataplex-datascan/iam.tf | 2 +- modules/dataplex-datascan/variables.tf | 3 +- modules/dataproc/README.md | 16 +++--- modules/dataproc/iam.tf | 2 +- modules/dataproc/variables.tf | 3 +- modules/dns/README.md | 14 +++--- modules/dns/variables.tf | 4 -- modules/folder/README.md | 22 ++++----- modules/folder/iam.tf | 2 +- modules/folder/variables.tf | 3 +- modules/iam-service-account/README.md | 24 ++++----- modules/iam-service-account/iam.tf | 2 +- modules/iam-service-account/variables.tf | 3 +- modules/kms/README.md | 24 ++++----- modules/kms/iam.tf | 18 +++---- modules/kms/variables.tf | 6 ++- modules/organization/README.md | 26 +++++----- modules/organization/iam.tf | 2 +- modules/organization/variables.tf | 3 +- modules/project/README.md | 49 ++++++++++--------- modules/project/iam.tf | 2 +- modules/project/variables.tf | 3 +- modules/source-repository/README.md | 10 ++-- modules/source-repository/iam.tf | 2 +- modules/source-repository/variables.tf | 3 +- .../project/examples/iam-bindings.yaml | 3 +- 32 files changed, 153 insertions(+), 144 deletions(-) diff --git a/fast/stages/0-bootstrap/organization.tf b/fast/stages/0-bootstrap/organization.tf index 946e3d7bb5..d9f622211b 100644 --- a/fast/stages/0-bootstrap/organization.tf +++ b/fast/stages/0-bootstrap/organization.tf @@ -88,8 +88,9 @@ module "organization" { ) # delegated role grant for resource manager service account iam_bindings = { - (module.organization.custom_role_id[var.custom_role_names.organization_iam_admin]) = { + organization_iam_admin_conditional = { members = [module.automation-tf-resman-sa.iam_email] + role = module.organization.custom_role_id[var.custom_role_names.organization_iam_admin] condition = { expression = format( "api.getAttribute('iam.googleapis.com/modifiedGrantsByRole', []).hasOnly([%s])", diff --git a/modules/__docs/20230816-iam-refactor.md b/modules/__docs/20230816-iam-refactor.md index 438252ac18..a5ad33799b 100644 --- a/modules/__docs/20230816-iam-refactor.md +++ b/modules/__docs/20230816-iam-refactor.md @@ -6,6 +6,7 @@ ## Status Implemented in [#1595](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1595). +Authoritative bindings type changed as per [#1622](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/issues/1622). ## Context @@ -39,15 +40,18 @@ The new `iam_bindings` variable will look like this: ```hcl variable "iam_bindings" { - description = "Authoritative IAM bindings with support for conditions, in {ROLE => { members = [], condition = {}}} format." + description = "Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary." type = map(object({ - members = list(string) + members = list(string) + role = string condition = optional(object({ expression = string title = string description = optional(string) })) })) + nullable = false + default = {} } ``` diff --git a/modules/data-catalog-policy-tag/README.md b/modules/data-catalog-policy-tag/README.md index b08a9feb64..8a46478426 100644 --- a/modules/data-catalog-policy-tag/README.md +++ b/modules/data-catalog-policy-tag/README.md @@ -79,17 +79,17 @@ module "cmn-dc" { | name | description | type | required | default | |---|---|:---:|:---:|:---:| -| [name](variables.tf#L76) | Name of this taxonomy. | string | ✓ | | -| [project_id](variables.tf#L91) | GCP project id. | | ✓ | | +| [name](variables.tf#L77) | Name of this taxonomy. | string | ✓ | | +| [project_id](variables.tf#L92) | 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_bindings](variables.tf#L41) | Authoritative IAM bindings in {ROLE => {members = [], condition = {}}}. | map(object({…})) | | {} | -| [iam_bindings_additive](variables.tf#L55) | Individual additive IAM bindings. Keys are arbitrary. | map(object({…})) | | {} | -| [location](variables.tf#L70) | Data Catalog Taxonomy location. | string | | "eu" | -| [prefix](variables.tf#L81) | Optional prefix used to generate project id and name. | string | | null | -| [tags](variables.tf#L95) | List of Data Catalog Policy tags to be created with optional IAM binging configuration in {tag => {ROLE => [MEMBERS]}} format. | map(object({…})) | | {} | +| [iam_bindings](variables.tf#L41) | Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary. | map(object({…})) | | {} | +| [iam_bindings_additive](variables.tf#L56) | Individual additive IAM bindings. Keys are arbitrary. | map(object({…})) | | {} | +| [location](variables.tf#L71) | Data Catalog Taxonomy location. | string | | "eu" | +| [prefix](variables.tf#L82) | Optional prefix used to generate project id and name. | string | | null | +| [tags](variables.tf#L96) | 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 268c0c58a9..06c307631e 100644 --- a/modules/data-catalog-policy-tag/iam.tf +++ b/modules/data-catalog-policy-tag/iam.tf @@ -53,7 +53,7 @@ resource "google_data_catalog_taxonomy_iam_binding" "bindings" { provider = google-beta for_each = var.iam_bindings taxonomy = google_data_catalog_taxonomy.default.id - role = each.key + role = each.value.role members = each.value.members dynamic "condition" { for_each = each.value.condition == null ? [] : [""] diff --git a/modules/data-catalog-policy-tag/variables.tf b/modules/data-catalog-policy-tag/variables.tf index b0df313d82..0fef9e7bd7 100644 --- a/modules/data-catalog-policy-tag/variables.tf +++ b/modules/data-catalog-policy-tag/variables.tf @@ -39,9 +39,10 @@ variable "iam" { } variable "iam_bindings" { - description = "Authoritative IAM bindings in {ROLE => {members = [], condition = {}}}." + description = "Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary." type = map(object({ members = list(string) + role = string condition = optional(object({ expression = string title = string diff --git a/modules/dataplex-datascan/README.md b/modules/dataplex-datascan/README.md index 1c950184e8..4116732f3d 100644 --- a/modules/dataplex-datascan/README.md +++ b/modules/dataplex-datascan/README.md @@ -431,9 +431,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#L157) | Name of Dataplex Scan. | string | ✓ | | +| [project_id](variables.tf#L168) | The ID of the project where the Dataplex DataScan will be created. | string | ✓ | | +| [region](variables.tf#L173) | 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 | @@ -441,11 +441,11 @@ module "dataplex-datascan" { | [execution_schedule](variables.tf#L94) | Schedule DataScan to run periodically based on a cron schedule expression. If not specified, the DataScan is created with `on_demand` schedule, which means it will not run until the user calls `dataScans.run` API. | string | | null | | [group_iam](variables.tf#L100) | 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#L107) | Dataplex DataScan IAM bindings in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | -| [iam_bindings](variables.tf#L114) | Authoritative IAM bindings in {ROLE => {members = [], condition = {}}}. | map(object({…})) | | {} | -| [iam_bindings_additive](variables.tf#L128) | Individual additive IAM bindings. Keys are arbitrary. | map(object({…})) | | {} | -| [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_bindings](variables.tf#L114) | Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary. | map(object({…})) | | {} | +| [iam_bindings_additive](variables.tf#L129) | Individual additive IAM bindings. Keys are arbitrary. | map(object({…})) | | {} | +| [incremental_field](variables.tf#L144) | 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#L150) | Resource labels. | map(string) | | {} | +| [prefix](variables.tf#L162) | 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 9a496ff182..9ed5914446 100644 --- a/modules/dataplex-datascan/iam.tf +++ b/modules/dataplex-datascan/iam.tf @@ -44,7 +44,7 @@ resource "google_dataplex_datascan_iam_binding" "bindings" { project = google_dataplex_datascan.datascan.project location = google_dataplex_datascan.datascan.location data_scan_id = google_dataplex_datascan.datascan.data_scan_id - role = each.key + role = each.value.role members = each.value.members dynamic "condition" { for_each = each.value.condition == null ? [] : [""] diff --git a/modules/dataplex-datascan/variables.tf b/modules/dataplex-datascan/variables.tf index 4e6b2bb133..a13cdc55e3 100644 --- a/modules/dataplex-datascan/variables.tf +++ b/modules/dataplex-datascan/variables.tf @@ -112,9 +112,10 @@ variable "iam" { } variable "iam_bindings" { - description = "Authoritative IAM bindings in {ROLE => {members = [], condition = {}}}." + description = "Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary." type = map(object({ members = list(string) + role = string condition = optional(object({ expression = string title = string diff --git a/modules/dataproc/README.md b/modules/dataproc/README.md index aa5326718e..5cd220cbaa 100644 --- a/modules/dataproc/README.md +++ b/modules/dataproc/README.md @@ -146,17 +146,17 @@ module "processing-dp-cluster" { | name | description | type | required | default | |---|---|:---:|:---:|:---:| -| [name](variables.tf#L234) | Cluster name. | string | ✓ | | -| [project_id](variables.tf#L249) | Project ID. | string | ✓ | | -| [region](variables.tf#L254) | Dataproc region. | string | ✓ | | +| [name](variables.tf#L235) | Cluster name. | string | ✓ | | +| [project_id](variables.tf#L250) | Project ID. | string | ✓ | | +| [region](variables.tf#L255) | 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_bindings](variables.tf#L199) | Authoritative IAM bindings in {ROLE => {members = [], condition = {}}}. | map(object({…})) | | {} | -| [iam_bindings_additive](variables.tf#L213) | Individual additive IAM bindings. Keys are arbitrary. | map(object({…})) | | {} | -| [labels](variables.tf#L228) | The resource labels for instance to use to annotate any related underlying resources, such as Compute Engine VMs. | map(string) | | {} | -| [prefix](variables.tf#L239) | Optional prefix used to generate project id and name. | string | | null | -| [service_account](variables.tf#L259) | Service account to set on the Dataproc cluster. | string | | null | +| [iam_bindings](variables.tf#L199) | Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary. | map(object({…})) | | {} | +| [iam_bindings_additive](variables.tf#L214) | Individual additive IAM bindings. Keys are arbitrary. | map(object({…})) | | {} | +| [labels](variables.tf#L229) | The resource labels for instance to use to annotate any related underlying resources, such as Compute Engine VMs. | map(string) | | {} | +| [prefix](variables.tf#L240) | Optional prefix used to generate project id and name. | string | | null | +| [service_account](variables.tf#L260) | Service account to set on the Dataproc cluster. | string | | null | ## Outputs diff --git a/modules/dataproc/iam.tf b/modules/dataproc/iam.tf index fba2eca9a7..ef0428d13c 100644 --- a/modules/dataproc/iam.tf +++ b/modules/dataproc/iam.tf @@ -46,7 +46,7 @@ resource "google_dataproc_cluster_iam_binding" "bindings" { project = var.project_id cluster = google_dataproc_cluster.cluster.name region = var.region - role = each.key + role = each.value.role members = each.value.members dynamic "condition" { for_each = each.value.condition == null ? [] : [""] diff --git a/modules/dataproc/variables.tf b/modules/dataproc/variables.tf index 49f4fa908b..8b77c5b96b 100644 --- a/modules/dataproc/variables.tf +++ b/modules/dataproc/variables.tf @@ -197,9 +197,10 @@ variable "iam" { } variable "iam_bindings" { - description = "Authoritative IAM bindings in {ROLE => {members = [], condition = {}}}." + description = "Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary." type = map(object({ members = list(string) + role = string condition = optional(object({ expression = string title = string diff --git a/modules/dns/README.md b/modules/dns/README.md index cdfff0e300..5b293768ea 100644 --- a/modules/dns/README.md +++ b/modules/dns/README.md @@ -140,17 +140,16 @@ module "public-dns" { # tftest modules=1 resources=4 inventory=public-zone.yaml ``` - ## Variables | name | description | type | required | default | |---|---|:---:|:---:|:---:| -| [name](variables.tf#L33) | Zone name, must be unique within the project. | string | ✓ | | -| [project_id](variables.tf#L38) | Project id for the zone. | string | ✓ | | -| [description](variables.tf#L21) | Domain description. | string | | "Terraform managed." | -| [iam](variables.tf#L27) | IAM bindings in {ROLE => [MEMBERS]} format. | map(list(string)) | | null | -| [recordsets](variables.tf#L43) | Map of DNS recordsets in \"type name\" => {ttl, [records]} format. | map(object({…})) | | {} | -| [zone_config](variables.tf#L78) | DNS zone configuration. | object({…}) | | null | +| [name](variables.tf#L29) | Zone name, must be unique within the project. | string | ✓ | | +| [project_id](variables.tf#L34) | Project id for the zone. | string | ✓ | | +| [description](variables.tf#L17) | Domain description. | string | | "Terraform managed." | +| [iam](variables.tf#L23) | IAM bindings in {ROLE => [MEMBERS]} format. | map(list(string)) | | null | +| [recordsets](variables.tf#L39) | Map of DNS recordsets in \"type name\" => {ttl, [records]} format. | map(object({…})) | | {} | +| [zone_config](variables.tf#L74) | DNS zone configuration. | object({…}) | | null | ## Outputs @@ -162,5 +161,4 @@ module "public-dns" { | [name](outputs.tf#L32) | The DNS zone name. | | | [name_servers](outputs.tf#L37) | The DNS zone name servers. | | | [zone](outputs.tf#L42) | DNS zone resource. | | - diff --git a/modules/dns/variables.tf b/modules/dns/variables.tf index 9c2bf54517..08395ba039 100644 --- a/modules/dns/variables.tf +++ b/modules/dns/variables.tf @@ -14,10 +14,6 @@ * limitations under the License. */ -############################################################################### -# zone variables # -############################################################################### - variable "description" { description = "Domain description." type = string diff --git a/modules/folder/README.md b/modules/folder/README.md index b4f4160182..65661210dc 100644 --- a/modules/folder/README.md +++ b/modules/folder/README.md @@ -290,17 +290,17 @@ module "folder" { | [folder_create](variables.tf#L33) | Create folder. When set to false, uses id to reference an existing folder. | bool | | true | | [group_iam](variables.tf#L39) | 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#L46) | IAM bindings in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | -| [iam_bindings](variables.tf#L53) | Authoritative IAM bindings in {ROLE => {members = [], condition = {}}}. | map(object({…})) | | {} | -| [iam_bindings_additive](variables.tf#L67) | Individual additive IAM bindings. Keys are arbitrary. | map(object({…})) | | {} | -| [id](variables.tf#L82) | Folder ID in case you use folder_create=false. | string | | null | -| [logging_data_access](variables.tf#L88) | 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#L103) | Logging exclusions for this folder in the form {NAME -> FILTER}. | map(string) | | {} | -| [logging_sinks](variables.tf#L110) | Logging sinks to create for the organization. | map(object({…})) | | {} | -| [name](variables.tf#L140) | Folder name. | string | | null | -| [org_policies](variables.tf#L146) | Organization policies applied to this folder keyed by policy name. | map(object({…})) | | {} | -| [org_policies_data_path](variables.tf#L173) | Path containing org policies in YAML format. | string | | null | -| [parent](variables.tf#L179) | Parent in folders/folder_id or organizations/org_id format. | string | | null | -| [tag_bindings](variables.tf#L189) | Tag bindings for this folder, in key => tag value id format. | map(string) | | null | +| [iam_bindings](variables.tf#L53) | Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary. | map(object({…})) | | {} | +| [iam_bindings_additive](variables.tf#L68) | Individual additive IAM bindings. Keys are arbitrary. | map(object({…})) | | {} | +| [id](variables.tf#L83) | Folder ID in case you use folder_create=false. | string | | null | +| [logging_data_access](variables.tf#L89) | 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#L104) | Logging exclusions for this folder in the form {NAME -> FILTER}. | map(string) | | {} | +| [logging_sinks](variables.tf#L111) | Logging sinks to create for the organization. | map(object({…})) | | {} | +| [name](variables.tf#L141) | Folder name. | string | | null | +| [org_policies](variables.tf#L147) | Organization policies applied to this folder keyed by policy name. | map(object({…})) | | {} | +| [org_policies_data_path](variables.tf#L174) | Path containing org policies in YAML format. | string | | null | +| [parent](variables.tf#L180) | Parent in folders/folder_id or organizations/org_id format. | string | | null | +| [tag_bindings](variables.tf#L190) | 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 976e312c71..20025b2831 100644 --- a/modules/folder/iam.tf +++ b/modules/folder/iam.tf @@ -42,7 +42,7 @@ resource "google_folder_iam_binding" "authoritative" { resource "google_folder_iam_binding" "bindings" { for_each = var.iam_bindings folder = local.folder.name - role = each.key + role = each.value.role members = each.value.members dynamic "condition" { for_each = each.value.condition == null ? [] : [""] diff --git a/modules/folder/variables.tf b/modules/folder/variables.tf index 619ee9c376..86efc21546 100644 --- a/modules/folder/variables.tf +++ b/modules/folder/variables.tf @@ -51,9 +51,10 @@ variable "iam" { } variable "iam_bindings" { - description = "Authoritative IAM bindings in {ROLE => {members = [], condition = {}}}." + description = "Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary." type = map(object({ members = list(string) + role = string condition = optional(object({ expression = string title = string diff --git a/modules/iam-service-account/README.md b/modules/iam-service-account/README.md index 9fd6cba01b..ea3362c73a 100644 --- a/modules/iam-service-account/README.md +++ b/modules/iam-service-account/README.md @@ -45,23 +45,23 @@ module "myproject-default-service-accounts" { | name | description | type | required | default | |---|---|:---:|:---:|:---:| -| [name](variables.tf#L113) | Name of the service account to create. | string | ✓ | | -| [project_id](variables.tf#L128) | Project id where service account will be created. | string | ✓ | | +| [name](variables.tf#L114) | Name of the service account to create. | string | ✓ | | +| [project_id](variables.tf#L129) | 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 | | [iam](variables.tf#L35) | IAM bindings on the service account in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | | [iam_billing_roles](variables.tf#L42) | Billing account roles granted to this service account, by billing account id. Non-authoritative. | map(list(string)) | | {} | -| [iam_bindings](variables.tf#L49) | Authoritative IAM bindings on the service account in {ROLE => {members = [], condition = {}}}. | map(object({…})) | | {} | -| [iam_bindings_additive](variables.tf#L63) | Individual additive IAM bindings on the service account. Keys are arbitrary. | map(object({…})) | | {} | -| [iam_folder_roles](variables.tf#L78) | Folder roles granted to this service account, by folder id. Non-authoritative. | map(list(string)) | | {} | -| [iam_organization_roles](variables.tf#L85) | Organization roles granted to this service account, by organization id. Non-authoritative. | map(list(string)) | | {} | -| [iam_project_roles](variables.tf#L92) | Project roles granted to this service account, by project id. | map(list(string)) | | {} | -| [iam_sa_roles](variables.tf#L99) | Service account roles granted to this service account, by service account name. | map(list(string)) | | {} | -| [iam_storage_roles](variables.tf#L106) | Storage roles granted to this service account, by bucket name. | map(list(string)) | | {} | -| [prefix](variables.tf#L118) | Prefix applied to service account names. | string | | null | -| [public_keys_directory](variables.tf#L133) | Path to public keys data files to upload to the service account (should have `.pem` extension). | string | | "" | -| [service_account_create](variables.tf#L139) | Create service account. When set to false, uses a data source to reference an existing service account. | bool | | true | +| [iam_bindings](variables.tf#L49) | Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary. | map(object({…})) | | {} | +| [iam_bindings_additive](variables.tf#L64) | Individual additive IAM bindings on the service account. Keys are arbitrary. | map(object({…})) | | {} | +| [iam_folder_roles](variables.tf#L79) | Folder roles granted to this service account, by folder id. Non-authoritative. | map(list(string)) | | {} | +| [iam_organization_roles](variables.tf#L86) | Organization roles granted to this service account, by organization id. Non-authoritative. | map(list(string)) | | {} | +| [iam_project_roles](variables.tf#L93) | Project roles granted to this service account, by project id. | map(list(string)) | | {} | +| [iam_sa_roles](variables.tf#L100) | Service account roles granted to this service account, by service account name. | map(list(string)) | | {} | +| [iam_storage_roles](variables.tf#L107) | Storage roles granted to this service account, by bucket name. | map(list(string)) | | {} | +| [prefix](variables.tf#L119) | Prefix applied to service account names. | string | | null | +| [public_keys_directory](variables.tf#L134) | Path to public keys data files to upload to the service account (should have `.pem` extension). | string | | "" | +| [service_account_create](variables.tf#L140) | 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 a9423fb0c5..15ae1acca7 100644 --- a/modules/iam-service-account/iam.tf +++ b/modules/iam-service-account/iam.tf @@ -71,7 +71,7 @@ resource "google_service_account_iam_binding" "authoritative" { resource "google_service_account_iam_binding" "bindings" { for_each = var.iam_bindings service_account_id = local.service_account.name - role = each.key + role = each.value.role members = each.value.members dynamic "condition" { for_each = each.value.condition == null ? [] : [""] diff --git a/modules/iam-service-account/variables.tf b/modules/iam-service-account/variables.tf index c9ca7069dd..4a75af462f 100644 --- a/modules/iam-service-account/variables.tf +++ b/modules/iam-service-account/variables.tf @@ -47,9 +47,10 @@ variable "iam_billing_roles" { } variable "iam_bindings" { - description = "Authoritative IAM bindings on the service account in {ROLE => {members = [], condition = {}}}." + description = "Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary." type = map(object({ members = list(string) + role = string condition = optional(object({ expression = string title = string diff --git a/modules/kms/README.md b/modules/kms/README.md index 56acff46e8..a3b2c902f9 100644 --- a/modules/kms/README.md +++ b/modules/kms/README.md @@ -89,19 +89,19 @@ module "kms" { | name | description | type | required | default | |---|---|:---:|:---:|:---:| -| [keyring](variables.tf#L117) | Keyring attributes. | object({…}) | ✓ | | -| [project_id](variables.tf#L140) | Project id where the keyring will be created. | string | ✓ | | +| [keyring](variables.tf#L119) | Keyring attributes. | object({…}) | ✓ | | +| [project_id](variables.tf#L142) | Project id where the keyring will be created. | string | ✓ | | | [iam](variables.tf#L17) | Keyring IAM bindings in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | -| [iam_bindings](variables.tf#L23) | Keyring authoritative IAM bindings in {ROLE => {members = [], condition = {}}}. | map(object({…})) | | {} | -| [iam_bindings_additive](variables.tf#L37) | Keyring individual additive IAM bindings. Keys are arbitrary. | map(object({…})) | | {} | -| [key_iam](variables.tf#L52) | Key IAM bindings in {KEY => {ROLE => [MEMBERS]}} format. | map(map(list(string))) | | {} | -| [key_iam_bindings](variables.tf#L58) | Key authoritative IAM bindings in {KEY => {ROLE => {members = [], condition = {}}}}. | map(object({…})) | | {} | -| [key_iam_bindings_additive](variables.tf#L72) | Key individual additive IAM bindings. Keys are arbitrary. | map(object({…})) | | {} | -| [key_purpose](variables.tf#L88) | 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#L100) | 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#L125) | Set to false to manage keys and IAM bindings in an existing keyring. | bool | | true | -| [keys](variables.tf#L131) | Key names and base attributes. Set attributes to null if not needed. | map(object({…})) | | {} | -| [tag_bindings](variables.tf#L145) | Tag bindings for this keyring, in key => tag value id format. | map(string) | | null | +| [iam_bindings](variables.tf#L23) | Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary. | map(object({…})) | | {} | +| [iam_bindings_additive](variables.tf#L38) | Keyring individual additive IAM bindings. Keys are arbitrary. | map(object({…})) | | {} | +| [key_iam](variables.tf#L53) | Key IAM bindings in {KEY => {ROLE => [MEMBERS]}} format. | map(map(list(string))) | | {} | +| [key_iam_bindings](variables.tf#L59) | Key authoritative IAM bindings in {KEY => {BINDING_KEY => {role = ROLE, members = [], condition = {}}}}. | map(object({…})) | | {} | +| [key_iam_bindings_additive](variables.tf#L74) | Key individual additive IAM bindings. Keys are arbitrary. | map(object({…})) | | {} | +| [key_purpose](variables.tf#L90) | 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#L102) | 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#L127) | Set to false to manage keys and IAM bindings in an existing keyring. | bool | | true | +| [keys](variables.tf#L133) | Key names and base attributes. Set attributes to null if not needed. | map(object({…})) | | {} | +| [tag_bindings](variables.tf#L147) | 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 9a78c2cfe4..ff8279a254 100644 --- a/modules/kms/iam.tf +++ b/modules/kms/iam.tf @@ -25,12 +25,13 @@ locals { ] ]) key_iam_bindings = flatten([ - for key, roles in var.key_iam_bindings : [ - for role, data in roles : { - key = key - role = role - members = data.members - condition = data.condition + for key, bindings in var.key_iam_bindings : [ + for binding_key, binding_data in bindings : { + key = key + binding_key = "${key}.${binding_key}" + role = binding_data.role + members = binding_data.members + condition = binding_data.condition } ] ]) @@ -46,7 +47,7 @@ resource "google_kms_key_ring_iam_binding" "authoritative" { resource "google_kms_key_ring_iam_binding" "bindings" { for_each = var.iam_bindings key_ring_id = local.keyring.id - role = each.key + role = each.value.role members = each.value.members dynamic "condition" { for_each = each.value.condition == null ? [] : [""] @@ -85,8 +86,7 @@ resource "google_kms_crypto_key_iam_binding" "authoritative" { resource "google_kms_crypto_key_iam_binding" "bindings" { for_each = { - for binding in local.key_iam_bindings : - "${binding.key}.${binding.role}" => binding + for binding in local.key_iam_bindings : binding.binding_key => binding } role = each.value.role crypto_key_id = google_kms_crypto_key.default[each.value.key].id diff --git a/modules/kms/variables.tf b/modules/kms/variables.tf index 44c980364c..93e42dc462 100644 --- a/modules/kms/variables.tf +++ b/modules/kms/variables.tf @@ -21,9 +21,10 @@ variable "iam" { } variable "iam_bindings" { - description = "Keyring authoritative IAM bindings in {ROLE => {members = [], condition = {}}}." + description = "Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary." type = map(object({ members = list(string) + role = string condition = optional(object({ expression = string title = string @@ -56,9 +57,10 @@ variable "key_iam" { } variable "key_iam_bindings" { - description = "Key authoritative IAM bindings in {KEY => {ROLE => {members = [], condition = {}}}}." + description = "Key authoritative IAM bindings in {KEY => {BINDING_KEY => {role = ROLE, members = [], condition = {}}}}." type = map(object({ members = list(string) + role = string condition = optional(object({ expression = string title = string diff --git a/modules/organization/README.md b/modules/organization/README.md index eb228dcf6b..fd9ca09435 100644 --- a/modules/organization/README.md +++ b/modules/organization/README.md @@ -446,24 +446,24 @@ module "org" { | name | description | type | required | default | |---|---|:---:|:---:|:---:| -| [organization_id](variables.tf#L210) | Organization id in organizations/nnnnnn format. | string | ✓ | | +| [organization_id](variables.tf#L211) | 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](variables.tf#L31) | Hierarchical firewall policies to associate to the organization. | object({…}) | | null | | [group_iam](variables.tf#L40) | 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#L47) | IAM bindings, in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | -| [iam_bindings](variables.tf#L54) | Authoritative IAM bindings in {ROLE => {members = [], condition = {}}}. | map(object({…})) | | {} | -| [iam_bindings_additive](variables.tf#L68) | Individual additive IAM bindings. Keys are arbitrary. | map(object({…})) | | {} | -| [logging_data_access](variables.tf#L83) | 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#L98) | Logging exclusions for this organization in the form {NAME -> FILTER}. | map(string) | | {} | -| [logging_sinks](variables.tf#L105) | Logging sinks to create for the organization. | map(object({…})) | | {} | -| [network_tags](variables.tf#L135) | 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#L157) | Organization policies applied to this organization keyed by policy name. | map(object({…})) | | {} | -| [org_policies_data_path](variables.tf#L184) | Path containing org policies in YAML format. | string | | null | -| [org_policy_custom_constraints](variables.tf#L190) | Organization policy custom constraints keyed by constraint name. | map(object({…})) | | {} | -| [org_policy_custom_constraints_data_path](variables.tf#L204) | Path containing org policy custom constraints in YAML format. | string | | null | -| [tag_bindings](variables.tf#L219) | Tag bindings for this organization, in key => tag value id format. | map(string) | | null | -| [tags](variables.tf#L225) | 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_bindings](variables.tf#L54) | Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary. | map(object({…})) | | {} | +| [iam_bindings_additive](variables.tf#L69) | Individual additive IAM bindings. Keys are arbitrary. | map(object({…})) | | {} | +| [logging_data_access](variables.tf#L84) | 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#L99) | Logging exclusions for this organization in the form {NAME -> FILTER}. | map(string) | | {} | +| [logging_sinks](variables.tf#L106) | Logging sinks to create for the organization. | map(object({…})) | | {} | +| [network_tags](variables.tf#L136) | 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#L158) | Organization policies applied to this organization keyed by policy name. | map(object({…})) | | {} | +| [org_policies_data_path](variables.tf#L185) | Path containing org policies in YAML format. | string | | null | +| [org_policy_custom_constraints](variables.tf#L191) | Organization policy custom constraints keyed by constraint name. | map(object({…})) | | {} | +| [org_policy_custom_constraints_data_path](variables.tf#L205) | Path containing org policy custom constraints in YAML format. | string | | null | +| [tag_bindings](variables.tf#L220) | Tag bindings for this organization, in key => tag value id format. | map(string) | | null | +| [tags](variables.tf#L226) | 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 2882d02a07..81a8d2b0ed 100644 --- a/modules/organization/iam.tf +++ b/modules/organization/iam.tf @@ -51,7 +51,7 @@ resource "google_organization_iam_binding" "authoritative" { resource "google_organization_iam_binding" "bindings" { for_each = var.iam_bindings org_id = local.organization_id_numeric - role = each.key + role = each.value.role members = each.value.members dynamic "condition" { for_each = each.value.condition == null ? [] : [""] diff --git a/modules/organization/variables.tf b/modules/organization/variables.tf index 99fe49c697..c9899e2e1d 100644 --- a/modules/organization/variables.tf +++ b/modules/organization/variables.tf @@ -52,9 +52,10 @@ variable "iam" { } variable "iam_bindings" { - description = "Authoritative IAM bindings in {ROLE => {members = [], condition = {}}}." + description = "Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary." type = map(object({ members = list(string) + role = string condition = optional(object({ expression = string title = string diff --git a/modules/project/README.md b/modules/project/README.md index 7479eca8cf..3fddf95f97 100644 --- a/modules/project/README.md +++ b/modules/project/README.md @@ -117,10 +117,11 @@ module "project" { "stackdriver.googleapis.com" ] iam_bindings = { - "roles/resourcemanager.projectIamAdmin" = { + iam_admin_conditional = { members = [ "group:test-admins@example.org" ] + role = "roles/resourcemanager.projectIamAdmin" condition = { title = "delegated_network_user_one" expression = <<-END @@ -589,7 +590,7 @@ output "compute_robot" { | name | description | type | required | default | |---|---|:---:|:---:|:---:| -| [name](variables.tf#L185) | Project name and id suffix. | string | ✓ | | +| [name](variables.tf#L186) | 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 | | [compute_metadata](variables.tf#L29) | Optional compute metadata key/values. Only usable if compute API has been enabled. | map(string) | | {} | @@ -599,28 +600,28 @@ output "compute_robot" { | [descriptive_name](variables.tf#L63) | Name of the project name. Used for project name instead of `name` variable. | string | | null | | [group_iam](variables.tf#L69) | 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#L76) | Authoritative IAM bindings in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | -| [iam_bindings](variables.tf#L83) | Authoritative IAM bindings in {ROLE => {members = [], condition = {}}}. | map(object({…})) | | {} | -| [iam_bindings_additive](variables.tf#L97) | Individual additive IAM bindings. Keys are arbitrary. | map(object({…})) | | {} | -| [labels](variables.tf#L112) | Resource labels. | map(string) | | {} | -| [lien_reason](variables.tf#L119) | If non-empty, creates a project lien with this description. | string | | null | -| [logging_data_access](variables.tf#L125) | 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#L140) | Logging exclusions for this project in the form {NAME -> FILTER}. | map(string) | | {} | -| [logging_sinks](variables.tf#L147) | Logging sinks to create for this project. | map(object({…})) | | {} | -| [metric_scopes](variables.tf#L178) | List of projects that will act as metric scopes for this project. | list(string) | | [] | -| [org_policies](variables.tf#L190) | Organization policies applied to this project keyed by policy name. | map(object({…})) | | {} | -| [org_policies_data_path](variables.tf#L217) | Path containing org policies in YAML format. | string | | null | -| [parent](variables.tf#L223) | Parent folder or organization in 'folders/folder_id' or 'organizations/org_id' format. | string | | null | -| [prefix](variables.tf#L233) | Optional prefix used to generate project id and name. | string | | null | -| [project_create](variables.tf#L243) | Create project. When set to false, uses a data source to reference existing project. | bool | | true | -| [service_config](variables.tf#L249) | Configure service API activation. | object({…}) | | {…} | -| [service_encryption_key_ids](variables.tf#L261) | Cloud KMS encryption key in {SERVICE => [KEY_URL]} format. | map(list(string)) | | {} | -| [service_perimeter_bridges](variables.tf#L268) | 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#L275) | Name of VPC-SC Standard perimeter to add project into. See comment in the variables file for format. | string | | null | -| [services](variables.tf#L281) | Service APIs to enable. | list(string) | | [] | -| [shared_vpc_host_config](variables.tf#L287) | Configures this project as a Shared VPC host project (mutually exclusive with shared_vpc_service_project). | object({…}) | | null | -| [shared_vpc_service_config](variables.tf#L296) | Configures this project as a Shared VPC service project (mutually exclusive with shared_vpc_host_config). | object({…}) | | {…} | -| [skip_delete](variables.tf#L318) | Allows the underlying resources to be destroyed without destroying the project itself. | bool | | false | -| [tag_bindings](variables.tf#L324) | Tag bindings for this project, in key => tag value id format. | map(string) | | null | +| [iam_bindings](variables.tf#L83) | Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary. | map(object({…})) | | {} | +| [iam_bindings_additive](variables.tf#L98) | Individual additive IAM bindings. Keys are arbitrary. | map(object({…})) | | {} | +| [labels](variables.tf#L113) | Resource labels. | map(string) | | {} | +| [lien_reason](variables.tf#L120) | If non-empty, creates a project lien with this description. | string | | null | +| [logging_data_access](variables.tf#L126) | 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#L141) | Logging exclusions for this project in the form {NAME -> FILTER}. | map(string) | | {} | +| [logging_sinks](variables.tf#L148) | Logging sinks to create for this project. | map(object({…})) | | {} | +| [metric_scopes](variables.tf#L179) | List of projects that will act as metric scopes for this project. | list(string) | | [] | +| [org_policies](variables.tf#L191) | Organization policies applied to this project keyed by policy name. | map(object({…})) | | {} | +| [org_policies_data_path](variables.tf#L218) | Path containing org policies in YAML format. | string | | null | +| [parent](variables.tf#L224) | Parent folder or organization in 'folders/folder_id' or 'organizations/org_id' format. | string | | null | +| [prefix](variables.tf#L234) | Optional prefix used to generate project id and name. | string | | null | +| [project_create](variables.tf#L244) | Create project. When set to false, uses a data source to reference existing project. | bool | | true | +| [service_config](variables.tf#L250) | Configure service API activation. | object({…}) | | {…} | +| [service_encryption_key_ids](variables.tf#L262) | Cloud KMS encryption key in {SERVICE => [KEY_URL]} format. | map(list(string)) | | {} | +| [service_perimeter_bridges](variables.tf#L269) | 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#L276) | Name of VPC-SC Standard perimeter to add project into. See comment in the variables file for format. | string | | null | +| [services](variables.tf#L282) | Service APIs to enable. | list(string) | | [] | +| [shared_vpc_host_config](variables.tf#L288) | Configures this project as a Shared VPC host project (mutually exclusive with shared_vpc_service_project). | object({…}) | | null | +| [shared_vpc_service_config](variables.tf#L297) | Configures this project as a Shared VPC service project (mutually exclusive with shared_vpc_host_config). | object({…}) | | {…} | +| [skip_delete](variables.tf#L319) | Allows the underlying resources to be destroyed without destroying the project itself. | bool | | false | +| [tag_bindings](variables.tf#L325) | 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 16f187d6c9..0f00f2861a 100644 --- a/modules/project/iam.tf +++ b/modules/project/iam.tf @@ -58,7 +58,7 @@ resource "google_project_iam_binding" "authoritative" { resource "google_project_iam_binding" "bindings" { for_each = var.iam_bindings project = local.project.project_id - role = each.key + role = each.value.role members = each.value.members dynamic "condition" { for_each = each.value.condition == null ? [] : [""] diff --git a/modules/project/variables.tf b/modules/project/variables.tf index 2824fcf38d..68f8b6c027 100644 --- a/modules/project/variables.tf +++ b/modules/project/variables.tf @@ -81,9 +81,10 @@ variable "iam" { } variable "iam_bindings" { - description = "Authoritative IAM bindings in {ROLE => {members = [], condition = {}}}." + description = "Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary." type = map(object({ members = list(string) + role = string condition = optional(object({ expression = string title = string diff --git a/modules/source-repository/README.md b/modules/source-repository/README.md index a62013fa98..c60ba7e46f 100644 --- a/modules/source-repository/README.md +++ b/modules/source-repository/README.md @@ -75,13 +75,13 @@ module "repo" { | name | description | type | required | default | |---|---|:---:|:---:|:---:| -| [name](variables.tf#L60) | Repository name. | string | ✓ | | -| [project_id](variables.tf#L65) | Project used for resources. | string | ✓ | | +| [name](variables.tf#L61) | Repository name. | string | ✓ | | +| [project_id](variables.tf#L66) | 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_bindings](variables.tf#L31) | Authoritative IAM bindings in {ROLE => {members = [], condition = {}}}. | map(object({…})) | | {} | -| [iam_bindings_additive](variables.tf#L45) | Individual additive IAM bindings. Keys are arbitrary. | map(object({…})) | | {} | -| [triggers](variables.tf#L70) | Cloud Build triggers. | map(object({…})) | | {} | +| [iam_bindings](variables.tf#L31) | Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary. | map(object({…})) | | {} | +| [iam_bindings_additive](variables.tf#L46) | Individual additive IAM bindings. Keys are arbitrary. | map(object({…})) | | {} | +| [triggers](variables.tf#L71) | Cloud Build triggers. | map(object({…})) | | {} | ## Outputs diff --git a/modules/source-repository/iam.tf b/modules/source-repository/iam.tf index be0cf6886a..1b225d1b29 100644 --- a/modules/source-repository/iam.tf +++ b/modules/source-repository/iam.tf @@ -44,7 +44,7 @@ resource "google_sourcerepo_repository_iam_binding" "bindings" { for_each = var.iam_bindings project = var.project_id repository = google_sourcerepo_repository.default.name - role = each.key + role = each.value.role members = each.value.members dynamic "condition" { for_each = each.value.condition == null ? [] : [""] diff --git a/modules/source-repository/variables.tf b/modules/source-repository/variables.tf index ce1c34e70d..23bfa789eb 100644 --- a/modules/source-repository/variables.tf +++ b/modules/source-repository/variables.tf @@ -29,9 +29,10 @@ variable "iam" { } variable "iam_bindings" { - description = "Authoritative IAM bindings in {ROLE => {members = [], condition = {}}}." + description = "Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary." type = map(object({ members = list(string) + role = string condition = optional(object({ expression = string title = string diff --git a/tests/modules/project/examples/iam-bindings.yaml b/tests/modules/project/examples/iam-bindings.yaml index f1f09e3654..c9fee92524 100644 --- a/tests/modules/project/examples/iam-bindings.yaml +++ b/tests/modules/project/examples/iam-bindings.yaml @@ -23,7 +23,7 @@ values: project_id: foo-project-example skip_delete: false timeouts: null - module.project.google_project_iam_binding.bindings["roles/resourcemanager.projectIamAdmin"]: + module.project.google_project_iam_binding.bindings["iam_admin_conditional"]: condition: - description: null expression: "api.getAttribute(\n 'iam.googleapis.com/modifiedGrantsByRole',\ @@ -54,4 +54,3 @@ counts: resources: 4 outputs: {} -