Add new `iam_members` variable to IAM additive module interfaces (#1589)

* resource management modules

* data catalog policy

* dataproc

* service account

* kms

* net-vpc

* source repository

* dataplex datascan

* service account module variable order
This commit is contained in:
Ludovico Magnocavallo 2023-08-14 11:54:50 +02:00 committed by GitHub
parent f9509ad6b7
commit adf2621727
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
38 changed files with 748 additions and 207 deletions

View File

@ -4,6 +4,15 @@ This module simplifies the creation of [Data Catalog](https://cloud.google.com/d
Note: Data Catalog is still in beta, hence this module currently uses the beta provider. Note: Data Catalog is still in beta, hence this module currently uses the beta provider.
<!-- BEGIN TOC -->
- [Examples](#examples)
- [Simple Taxonomy with policy tags](#simple-taxonomy-with-policy-tags)
- [Taxonomy with IAM binding](#taxonomy-with-iam-binding)
- [Variables](#variables)
- [Outputs](#outputs)
- [TODO](#todo)
<!-- END TOC -->
## Examples ## Examples
### Simple Taxonomy with policy tags ### Simple Taxonomy with policy tags
@ -43,25 +52,32 @@ module "cmn-dc" {
iam = { iam = {
"roles/datacatalog.categoryAdmin" = ["group:GROUP_NAME@example.com"] "roles/datacatalog.categoryAdmin" = ["group:GROUP_NAME@example.com"]
} }
iam_members = {
am1-admin = {
member = "user:am1@example.com"
role = "roles/datacatalog.categoryAdmin"
} }
# tftest modules=1 resources=6 }
}
# tftest modules=1 resources=7
``` ```
<!-- BEGIN TFDOC --> <!-- BEGIN TFDOC -->
## Variables ## Variables
| name | description | type | required | default | | name | description | type | required | default |
|---|---|:---:|:---:|:---:| |---|---|:---:|:---:|:---:|
| [name](variables.tf#L59) | Name of this taxonomy. | <code>string</code> | ✓ | | | [name](variables.tf#L69) | Name of this taxonomy. | <code>string</code> | ✓ | |
| [project_id](variables.tf#L74) | GCP project id. | <code></code> | ✓ | | | [project_id](variables.tf#L84) | GCP project id. | <code></code> | ✓ | |
| [activated_policy_types](variables.tf#L17) | A list of policy types that are activated for this taxonomy. | <code>list&#40;string&#41;</code> | | <code>&#91;&#34;FINE_GRAINED_ACCESS_CONTROL&#34;&#93;</code> | | [activated_policy_types](variables.tf#L17) | A list of policy types that are activated for this taxonomy. | <code>list&#40;string&#41;</code> | | <code>&#91;&#34;FINE_GRAINED_ACCESS_CONTROL&#34;&#93;</code> |
| [description](variables.tf#L23) | Description of this taxonomy. | <code>string</code> | | <code>&#34;Taxonomy - Terraform managed&#34;</code> | | [description](variables.tf#L23) | Description of this taxonomy. | <code>string</code> | | <code>&#34;Taxonomy - Terraform managed&#34;</code> |
| [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. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> | | [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. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam](variables.tf#L35) | IAM bindings in {ROLE => [MEMBERS]} format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> | | [iam](variables.tf#L35) | IAM bindings in {ROLE => [MEMBERS]} format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam_additive](variables.tf#L41) | IAM additive bindings in {ROLE => [MEMBERS]} format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> | | [iam_additive](variables.tf#L41) | IAM additive bindings in {ROLE => [MEMBERS]} format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam_additive_members](variables.tf#L47) | IAM additive bindings in {MEMBERS => [ROLE]} format. This might break if members are dynamic values. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> | | [iam_additive_members](variables.tf#L47) | IAM additive bindings in {MEMBERS => [ROLE]} format. This might break if members are dynamic values. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [location](variables.tf#L53) | Data Catalog Taxonomy location. | <code>string</code> | | <code>&#34;eu&#34;</code> | | [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. | <code title="map&#40;object&#40;&#123;&#10; member &#61; string&#10; role &#61; string&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [prefix](variables.tf#L64) | Optional prefix used to generate project id and name. | <code>string</code> | | <code>null</code> | | [location](variables.tf#L63) | Data Catalog Taxonomy location. | <code>string</code> | | <code>&#34;eu&#34;</code> |
| [tags](variables.tf#L78) | List of Data Catalog Policy tags to be created with optional IAM binging configuration in {tag => {ROLE => [MEMBERS]}} format. | <code title="map&#40;object&#40;&#123;&#10; description &#61; optional&#40;string&#41;&#10; iam &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> | | [prefix](variables.tf#L74) | Optional prefix used to generate project id and name. | <code>string</code> | | <code>null</code> |
| [tags](variables.tf#L88) | List of Data Catalog Policy tags to be created with optional IAM binging configuration in {tag => {ROLE => [MEMBERS]}} format. | <code title="map&#40;object&#40;&#123;&#10; description &#61; optional&#40;string&#41;&#10; iam &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
## Outputs ## Outputs

View File

@ -58,9 +58,9 @@ locals {
resource "google_data_catalog_taxonomy_iam_binding" "authoritative" { resource "google_data_catalog_taxonomy_iam_binding" "authoritative" {
provider = google-beta provider = google-beta
for_each = local.iam for_each = local.iam
taxonomy = google_data_catalog_taxonomy.default.id
role = each.key role = each.key
members = each.value members = each.value
taxonomy = google_data_catalog_taxonomy.default.id
} }
resource "google_data_catalog_taxonomy_iam_member" "additive" { resource "google_data_catalog_taxonomy_iam_member" "additive" {
@ -70,9 +70,16 @@ resource "google_data_catalog_taxonomy_iam_member" "additive" {
? local.iam_additive ? local.iam_additive
: {} : {}
) )
taxonomy = google_data_catalog_taxonomy.default.id
role = each.value.role role = each.value.role
member = each.value.member member = each.value.member
}
resource "google_data_catalog_taxonomy_iam_member" "members" {
for_each = var.iam_members
taxonomy = google_data_catalog_taxonomy.default.id taxonomy = google_data_catalog_taxonomy.default.id
role = each.value.role
member = each.value.member
} }
resource "google_data_catalog_policy_tag_iam_binding" "authoritative" { resource "google_data_catalog_policy_tag_iam_binding" "authoritative" {
@ -80,7 +87,6 @@ resource "google_data_catalog_policy_tag_iam_binding" "authoritative" {
for_each = { for_each = {
for v in local.tags_iam : "${v.tag}.${v.role}" => v for v in local.tags_iam : "${v.tag}.${v.role}" => v
} }
policy_tag = google_data_catalog_policy_tag.default[each.value.tag].name policy_tag = google_data_catalog_policy_tag.default[each.value.tag].name
role = each.value.role role = each.value.role
members = each.value.members members = each.value.members

View File

@ -50,6 +50,16 @@ variable "iam_additive_members" {
default = {} default = {}
} }
variable "iam_members" {
description = "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."
type = map(object({
member = string
role = string
}))
nullable = false
default = {}
}
variable "location" { variable "location" {
description = "Data Catalog Taxonomy location." description = "Data Catalog Taxonomy location."
type = string type = string

View File

@ -2,9 +2,20 @@
This module manages the creation of Dataplex DataScan resources. This module manages the creation of Dataplex DataScan resources.
<!-- BEGIN TOC -->
- [Data Profiling](#data-profiling)
- [Data Quality](#data-quality)
- [Data Source](#data-source)
- [Execution Schedule](#execution-schedule)
- [IAM](#iam)
- [TODO](#todo)
- [Variables](#variables)
- [Outputs](#outputs)
<!-- END TOC -->
## Data Profiling ## Data Profiling
This example shows how to create a Data Profiling scan. To create an Data Profiling scan, provide the `data_profile_spec` input arguments as documented in https://cloud.google.com/dataplex/docs/reference/rest/v1/DataProfileSpec. This example shows how to create a Data Profiling scan. To create an Data Profiling scan, provide the `data_profile_spec` input arguments as documented in <https://cloud.google.com/dataplex/docs/reference/rest/v1/DataProfileSpec>.
```hcl ```hcl
module "dataplex-datascan" { module "dataplex-datascan" {
@ -30,9 +41,9 @@ module "dataplex-datascan" {
## Data Quality ## Data Quality
To create an Data Quality scan, provide the `data_quality_spec` input arguments as documented in https://cloud.google.com/dataplex/docs/reference/rest/v1/DataQualitySpec. To create an Data Quality scan, provide the `data_quality_spec` input arguments as documented in <https://cloud.google.com/dataplex/docs/reference/rest/v1/DataQualitySpec>.
Documentation for the supported rule types and rule specifications can be found in https://cloud.example.com/dataplex/docs/reference/rest/v1/DataQualityRule. Documentation for the supported rule types and rule specifications can be found in <https://cloud.example.com/dataplex/docs/reference/rest/v1/DataQualityRule>.
This example shows how to create a Data Quality scan. This example shows how to create a Data Quality scan.
@ -304,6 +315,7 @@ rules:
The input variable 'data' is required to create a DataScan. This value is immutable. Once it is set, you cannot change the DataScan to another source. The input variable 'data' is required to create a DataScan. This value is immutable. Once it is set, you cannot change the DataScan to another source.
The input variable 'data' should be an object containing a single key-value pair that can be one of: The input variable 'data' should be an object containing a single key-value pair that can be one of:
* `entity`: The Dataplex entity that represents the data source (e.g. BigQuery table) for DataScan, of the form: `projects/{project_number}/locations/{locationId}/lakes/{lakeId}/zones/{zoneId}/entities/{entityId}`. * `entity`: The Dataplex entity that represents the data source (e.g. BigQuery table) for DataScan, of the form: `projects/{project_number}/locations/{locationId}/lakes/{lakeId}/zones/{zoneId}/entities/{entityId}`.
* `resource`: The service-qualified full resource name of the cloud resource for a DataScan job to scan against. The field could be: BigQuery table of type "TABLE" for DataProfileScan/DataQualityScan format, e.g: `//bigquery.googleapis.com/projects/PROJECT_ID/datasets/DATASET_ID/tables/TABLE_ID`. * `resource`: The service-qualified full resource name of the cloud resource for a DataScan job to scan against. The field could be: BigQuery table of type "TABLE" for DataProfileScan/DataQualityScan format, e.g: `//bigquery.googleapis.com/projects/PROJECT_ID/datasets/DATASET_ID/tables/TABLE_ID`.
@ -368,17 +380,17 @@ module "dataplex-datascan" {
## IAM ## IAM
There are three mutually exclusive ways of managing IAM in this module IAM is managed via several variables that implement different levels of control:
- non-authoritative via the `iam_additive` and `iam_additive_members` variables, where bindings created outside this module will coexist with those managed here * `group_iam` and `iam` configure authoritative bindings that manage individual roles exclusively, mapping to the [`google_project_iam_binding`](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/google_project_iam#google_project_iam_binding) resource
- authoritative via the `group_iam` and `iam` variables, where bindings created outside this module (eg in the console) will be removed at each `terraform apply` cycle if the same role is also managed here * `iam_additive`, `iam_additive_members` and `iam_members` configure additive bindings that only manage individual role/member pairs, mapping to the [`google_project_iam_member`](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/google_project_iam#google_project_iam_member) resource
- authoritative policy via the `iam_policy` variable, where any binding created outside this module (eg in the console) will be removed at each `terraform apply` cycle regardless of the role * `iam_policy` which controls the entire IAM policy for the project, where any binding created outside this module (eg in the console) will be removed at each `terraform apply` cycle regardless of the role
The authoritative and additive approaches can be used together, provided different roles are managed by each. The IAM policy is incompatible with the other approaches, and must be used with extreme care. The authoritative and additive approaches can be used together, provided different roles are managed by each. The IAM policy is incompatible with the other approaches, and must be used with extreme care.
Some care must also be taken with the `group_iam` variable (and in some situations with the additive variables) to ensure that variable keys are static values, so that Terraform is able to compute the dependency graph. Some care must also be taken with the `group_iam` and `iam_additive_*` variables to ensure that variable keys are static values, so that Terraform is able to compute the dependency graph. For additive roles `iam_members` ensures that no dynamic values are used in the internal loop.
An example is provided beow for using `group_iam` and `iam` variables. An example is provided below for using some of these variables.
```hcl ```hcl
module "dataplex-datascan" { module "dataplex-datascan" {
@ -404,8 +416,14 @@ module "dataplex-datascan" {
"roles/dataplex.dataScanViewer" "roles/dataplex.dataScanViewer"
] ]
} }
iam_members = {
am1-viewer = {
member = "user:am1@example.com"
role = "roles/dataplex.dataScanViewer"
} }
# tftest modules=1 resources=4 inventory=datascan_iam.yaml }
}
# tftest modules=1 resources=5 inventory=datascan_iam.yaml
``` ```
## TODO ## TODO
@ -415,9 +433,9 @@ module "dataplex-datascan" {
| name | description | type | required | default | | 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`. | <code title="object&#40;&#123;&#10; entity &#61; optional&#40;string&#41;&#10; resource &#61; optional&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | | [data](variables.tf#L17) | The data source for DataScan. The source can be either a Dataplex `entity` or a BigQuery `resource`. | <code title="object&#40;&#123;&#10; entity &#61; optional&#40;string&#41;&#10; resource &#61; optional&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | |
| [name](variables.tf#L146) | Name of Dataplex Scan. | <code>string</code> | ✓ | | | [name](variables.tf#L156) | Name of Dataplex Scan. | <code>string</code> | ✓ | |
| [project_id](variables.tf#L157) | The ID of the project where the Dataplex DataScan will be created. | <code>string</code> | ✓ | | | [project_id](variables.tf#L167) | The ID of the project where the Dataplex DataScan will be created. | <code>string</code> | ✓ | |
| [region](variables.tf#L162) | Region for the Dataplex DataScan. | <code>string</code> | ✓ | | | [region](variables.tf#L172) | Region for the Dataplex DataScan. | <code>string</code> | ✓ | |
| [data_profile_spec](variables.tf#L29) | DataProfileScan related setting. Variable descriptions are provided in https://cloud.google.com/dataplex/docs/reference/rest/v1/DataProfileSpec. | <code title="object&#40;&#123;&#10; sampling_percent &#61; optional&#40;number&#41;&#10; row_filter &#61; optional&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | | [data_profile_spec](variables.tf#L29) | DataProfileScan related setting. Variable descriptions are provided in https://cloud.google.com/dataplex/docs/reference/rest/v1/DataProfileSpec. | <code title="object&#40;&#123;&#10; sampling_percent &#61; optional&#40;number&#41;&#10; row_filter &#61; optional&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [data_quality_spec](variables.tf#L38) | DataQualityScan related setting. Variable descriptions are provided in https://cloud.google.com/dataplex/docs/reference/rest/v1/DataQualitySpec. | <code title="object&#40;&#123;&#10; sampling_percent &#61; optional&#40;number&#41;&#10; row_filter &#61; optional&#40;string&#41;&#10; rules &#61; list&#40;object&#40;&#123;&#10; column &#61; optional&#40;string&#41;&#10; ignore_null &#61; optional&#40;bool, null&#41;&#10; dimension &#61; string&#10; threshold &#61; optional&#40;number&#41;&#10; non_null_expectation &#61; optional&#40;object&#40;&#123;&#125;&#41;&#41;&#10; range_expectation &#61; optional&#40;object&#40;&#123;&#10; min_value &#61; optional&#40;number&#41;&#10; max_value &#61; optional&#40;number&#41;&#10; strict_min_enabled &#61; optional&#40;bool&#41;&#10; strict_max_enabled &#61; optional&#40;bool&#41;&#10; &#125;&#41;&#41;&#10; regex_expectation &#61; optional&#40;object&#40;&#123;&#10; regex &#61; string&#10; &#125;&#41;&#41;&#10; set_expectation &#61; optional&#40;object&#40;&#123;&#10; values &#61; list&#40;string&#41;&#10; &#125;&#41;&#41;&#10; uniqueness_expectation &#61; optional&#40;object&#40;&#123;&#125;&#41;&#41;&#10; statistic_range_expectation &#61; optional&#40;object&#40;&#123;&#10; statistic &#61; string&#10; min_value &#61; optional&#40;number&#41;&#10; max_value &#61; optional&#40;number&#41;&#10; strict_min_enabled &#61; optional&#40;bool&#41;&#10; strict_max_enabled &#61; optional&#40;bool&#41;&#10; &#125;&#41;&#41;&#10; row_condition_expectation &#61; optional&#40;object&#40;&#123;&#10; sql_expression &#61; string&#10; &#125;&#41;&#41;&#10; table_condition_expectation &#61; optional&#40;object&#40;&#123;&#10; sql_expression &#61; string&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | | [data_quality_spec](variables.tf#L38) | DataQualityScan related setting. Variable descriptions are provided in https://cloud.google.com/dataplex/docs/reference/rest/v1/DataQualitySpec. | <code title="object&#40;&#123;&#10; sampling_percent &#61; optional&#40;number&#41;&#10; row_filter &#61; optional&#40;string&#41;&#10; rules &#61; list&#40;object&#40;&#123;&#10; column &#61; optional&#40;string&#41;&#10; ignore_null &#61; optional&#40;bool, null&#41;&#10; dimension &#61; string&#10; threshold &#61; optional&#40;number&#41;&#10; non_null_expectation &#61; optional&#40;object&#40;&#123;&#125;&#41;&#41;&#10; range_expectation &#61; optional&#40;object&#40;&#123;&#10; min_value &#61; optional&#40;number&#41;&#10; max_value &#61; optional&#40;number&#41;&#10; strict_min_enabled &#61; optional&#40;bool&#41;&#10; strict_max_enabled &#61; optional&#40;bool&#41;&#10; &#125;&#41;&#41;&#10; regex_expectation &#61; optional&#40;object&#40;&#123;&#10; regex &#61; string&#10; &#125;&#41;&#41;&#10; set_expectation &#61; optional&#40;object&#40;&#123;&#10; values &#61; list&#40;string&#41;&#10; &#125;&#41;&#41;&#10; uniqueness_expectation &#61; optional&#40;object&#40;&#123;&#125;&#41;&#41;&#10; statistic_range_expectation &#61; optional&#40;object&#40;&#123;&#10; statistic &#61; string&#10; min_value &#61; optional&#40;number&#41;&#10; max_value &#61; optional&#40;number&#41;&#10; strict_min_enabled &#61; optional&#40;bool&#41;&#10; strict_max_enabled &#61; optional&#40;bool&#41;&#10; &#125;&#41;&#41;&#10; row_condition_expectation &#61; optional&#40;object&#40;&#123;&#10; sql_expression &#61; string&#10; &#125;&#41;&#41;&#10; table_condition_expectation &#61; optional&#40;object&#40;&#123;&#10; sql_expression &#61; string&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [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. | <code title="object&#40;&#123;&#10; path &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | | [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. | <code title="object&#40;&#123;&#10; path &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
@ -427,10 +445,11 @@ module "dataplex-datascan" {
| [iam](variables.tf#L107) | Dataplex DataScan IAM bindings in {ROLE => [MEMBERS]} format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> | | [iam](variables.tf#L107) | Dataplex DataScan IAM bindings in {ROLE => [MEMBERS]} format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam_additive](variables.tf#L114) | IAM additive bindings in {ROLE => [MEMBERS]} format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> | | [iam_additive](variables.tf#L114) | IAM additive bindings in {ROLE => [MEMBERS]} format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam_additive_members](variables.tf#L121) | IAM additive bindings in {MEMBERS => [ROLE]} format. This might break if members are dynamic values. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> | | [iam_additive_members](variables.tf#L121) | IAM additive bindings in {MEMBERS => [ROLE]} format. This might break if members are dynamic values. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam_policy](variables.tf#L127) | IAM authoritative policy in {ROLE => [MEMBERS]} format. Roles and members not explicitly listed will be cleared, use with extreme caution. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>null</code> | | [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. | <code title="map&#40;object&#40;&#123;&#10; member &#61; string&#10; role &#61; string&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [incremental_field](variables.tf#L133) | 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. | <code>string</code> | | <code>null</code> | | [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. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>null</code> |
| [labels](variables.tf#L139) | Resource labels. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> | | [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. | <code>string</code> | | <code>null</code> |
| [prefix](variables.tf#L151) | Optional prefix used to generate Dataplex DataScan ID. | <code>string</code> | | <code>null</code> | | [labels](variables.tf#L149) | Resource labels. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> |
| [prefix](variables.tf#L161) | Optional prefix used to generate Dataplex DataScan ID. | <code>string</code> | | <code>null</code> |
## Outputs ## Outputs

View File

@ -69,6 +69,15 @@ resource "google_dataplex_datascan_iam_member" "additive" {
member = each.value.member member = each.value.member
} }
resource "google_dataplex_datascan_iam_member" "members" {
for_each = var.iam_members
project = google_dataplex_datascan.datascan.project
location = google_dataplex_datascan.datascan.location
data_scan_id = google_dataplex_datascan.datascan.data_scan_id
role = each.value.role
member = each.value.member
}
resource "google_dataplex_datascan_iam_policy" "authoritative_for_resource" { resource "google_dataplex_datascan_iam_policy" "authoritative_for_resource" {
count = var.iam_policy != null ? 1 : 0 count = var.iam_policy != null ? 1 : 0
project = google_dataplex_datascan.datascan.project project = google_dataplex_datascan.datascan.project

View File

@ -124,6 +124,16 @@ variable "iam_additive_members" {
default = {} default = {}
} }
variable "iam_members" {
description = "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."
type = map(object({
member = string
role = string
}))
nullable = false
default = {}
}
variable "iam_policy" { variable "iam_policy" {
description = "IAM authoritative policy in {ROLE => [MEMBERS]} format. Roles and members not explicitly listed will be cleared, use with extreme caution." description = "IAM authoritative policy in {ROLE => [MEMBERS]} format. Roles and members not explicitly listed will be cleared, use with extreme caution."
type = map(list(string)) type = map(list(string))

File diff suppressed because one or more lines are too long

View File

@ -65,3 +65,11 @@ resource "google_dataproc_cluster_iam_member" "additive" {
role = each.value.role role = each.value.role
member = each.value.member member = each.value.member
} }
resource "google_dataproc_cluster_iam_member" "members" {
for_each = var.iam_members
project = var.project_id
cluster = google_dataproc_cluster.cluster.name
role = each.value.role
member = each.value.member
}

View File

@ -203,6 +203,16 @@ variable "iam_additive" {
nullable = false nullable = false
} }
variable "iam_members" {
description = "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."
type = map(object({
member = string
role = string
}))
nullable = false
default = {}
}
variable "labels" { variable "labels" {
description = "The resource labels for instance to use to annotate any related underlying resources, such as Compute Engine VMs." description = "The resource labels for instance to use to annotate any related underlying resources, such as Compute Engine VMs."
type = map(string) type = map(string)

View File

@ -41,15 +41,21 @@ module "folder" {
"user:am1@example.org" = ["roles/storage.admin"] "user:am1@example.org" = ["roles/storage.admin"]
"user:am2@example.org" = ["roles/storage.objectViewer"] "user:am2@example.org" = ["roles/storage.objectViewer"]
} }
iam_members = {
am1-storage-admin = {
member = "user:am1@example.org"
role = "roles/storage.admin"
} }
# tftest modules=1 resources=9 inventory=iam.yaml }
}
# tftest modules=1 resources=10 inventory=iam.yaml
``` ```
## IAM ## IAM
There are three mutually exclusive ways at the role level of managing IAM in this module There are four three exclusive ways at the role level of managing IAM in this module
- non-authoritative via the `iam_additive` and `iam_additive_members` variables, where bindings created outside this module will coexist with those managed here - non-authoritative via the `iam_additive`, `iam_additive_members` and `iam_members` variables, where bindings created outside this module will coexist with those managed here
- authoritative via the `group_iam` and `iam` variables, where bindings created outside this module (eg in the console) will be removed at each `terraform apply` cycle if the same role is also managed here - authoritative via the `group_iam` and `iam` variables, where bindings created outside this module (eg in the console) will be removed at each `terraform apply` cycle if the same role is also managed here
- authoritative policy via the `iam_policy` variable, where any binding created outside this module (eg in the console) will be removed at each `terraform apply` cycle regardless of the role - authoritative policy via the `iam_policy` variable, where any binding created outside this module (eg in the console) will be removed at each `terraform apply` cycle regardless of the role
@ -319,16 +325,17 @@ module "folder" {
| [iam](variables.tf#L44) | IAM bindings in {ROLE => [MEMBERS]} format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> | | [iam](variables.tf#L44) | IAM bindings in {ROLE => [MEMBERS]} format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam_additive](variables.tf#L51) | Non authoritative IAM bindings, in {ROLE => [MEMBERS]} format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> | | [iam_additive](variables.tf#L51) | Non authoritative IAM bindings, in {ROLE => [MEMBERS]} format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam_additive_members](variables.tf#L58) | IAM additive bindings in {MEMBERS => [ROLE]} format. This might break if members are dynamic values. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> | | [iam_additive_members](variables.tf#L58) | IAM additive bindings in {MEMBERS => [ROLE]} format. This might break if members are dynamic values. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam_policy](variables.tf#L65) | IAM authoritative policy in {ROLE => [MEMBERS]} format. Roles and members not explicitly listed will be cleared, use with extreme caution. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>null</code> | | [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. | <code title="map&#40;object&#40;&#123;&#10; member &#61; string&#10; role &#61; string&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [id](variables.tf#L71) | Folder ID in case you use folder_create=false. | <code>string</code> | | <code>null</code> | | [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. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>null</code> |
| [logging_data_access](variables.tf#L77) | Control activation of data access logs. Format is service => { log type => [exempted members]}. The special 'allServices' key denotes configuration for all services. | <code>map&#40;map&#40;list&#40;string&#41;&#41;&#41;</code> | | <code>&#123;&#125;</code> | | [id](variables.tf#L81) | Folder ID in case you use folder_create=false. | <code>string</code> | | <code>null</code> |
| [logging_exclusions](variables.tf#L92) | Logging exclusions for this folder in the form {NAME -> FILTER}. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> | | [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. | <code>map&#40;map&#40;list&#40;string&#41;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [logging_sinks](variables.tf#L99) | Logging sinks to create for the organization. | <code title="map&#40;object&#40;&#123;&#10; bq_partitioned_table &#61; optional&#40;bool&#41;&#10; description &#61; optional&#40;string&#41;&#10; destination &#61; string&#10; disabled &#61; optional&#40;bool, false&#41;&#10; exclusions &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; filter &#61; string&#10; include_children &#61; optional&#40;bool, true&#41;&#10; type &#61; string&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> | | [logging_exclusions](variables.tf#L102) | Logging exclusions for this folder in the form {NAME -> FILTER}. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> |
| [name](variables.tf#L129) | Folder name. | <code>string</code> | | <code>null</code> | | [logging_sinks](variables.tf#L109) | Logging sinks to create for the organization. | <code title="map&#40;object&#40;&#123;&#10; bq_partitioned_table &#61; optional&#40;bool&#41;&#10; description &#61; optional&#40;string&#41;&#10; destination &#61; string&#10; disabled &#61; optional&#40;bool, false&#41;&#10; exclusions &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; filter &#61; string&#10; include_children &#61; optional&#40;bool, true&#41;&#10; type &#61; string&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [org_policies](variables.tf#L135) | Organization policies applied to this folder keyed by policy name. | <code title="map&#40;object&#40;&#123;&#10; inherit_from_parent &#61; optional&#40;bool&#41; &#35; for list policies only.&#10; reset &#61; optional&#40;bool&#41;&#10; rules &#61; optional&#40;list&#40;object&#40;&#123;&#10; allow &#61; optional&#40;object&#40;&#123;&#10; all &#61; optional&#40;bool&#41;&#10; values &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#41;&#10; deny &#61; optional&#40;object&#40;&#123;&#10; all &#61; optional&#40;bool&#41;&#10; values &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#41;&#10; enforce &#61; optional&#40;bool&#41; &#35; for boolean policies only.&#10; condition &#61; optional&#40;object&#40;&#123;&#10; description &#61; optional&#40;string&#41;&#10; expression &#61; optional&#40;string&#41;&#10; location &#61; optional&#40;string&#41;&#10; title &#61; optional&#40;string&#41;&#10; &#125;&#41;, &#123;&#125;&#41;&#10; &#125;&#41;&#41;, &#91;&#93;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> | | [name](variables.tf#L139) | Folder name. | <code>string</code> | | <code>null</code> |
| [org_policies_data_path](variables.tf#L162) | Path containing org policies in YAML format. | <code>string</code> | | <code>null</code> | | [org_policies](variables.tf#L145) | Organization policies applied to this folder keyed by policy name. | <code title="map&#40;object&#40;&#123;&#10; inherit_from_parent &#61; optional&#40;bool&#41; &#35; for list policies only.&#10; reset &#61; optional&#40;bool&#41;&#10; rules &#61; optional&#40;list&#40;object&#40;&#123;&#10; allow &#61; optional&#40;object&#40;&#123;&#10; all &#61; optional&#40;bool&#41;&#10; values &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#41;&#10; deny &#61; optional&#40;object&#40;&#123;&#10; all &#61; optional&#40;bool&#41;&#10; values &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#41;&#10; enforce &#61; optional&#40;bool&#41; &#35; for boolean policies only.&#10; condition &#61; optional&#40;object&#40;&#123;&#10; description &#61; optional&#40;string&#41;&#10; expression &#61; optional&#40;string&#41;&#10; location &#61; optional&#40;string&#41;&#10; title &#61; optional&#40;string&#41;&#10; &#125;&#41;, &#123;&#125;&#41;&#10; &#125;&#41;&#41;, &#91;&#93;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [parent](variables.tf#L168) | Parent in folders/folder_id or organizations/org_id format. | <code>string</code> | | <code>null</code> | | [org_policies_data_path](variables.tf#L172) | Path containing org policies in YAML format. | <code>string</code> | | <code>null</code> |
| [tag_bindings](variables.tf#L178) | Tag bindings for this folder, in key => tag value id format. | <code>map&#40;string&#41;</code> | | <code>null</code> | | [parent](variables.tf#L178) | Parent in folders/folder_id or organizations/org_id format. | <code>string</code> | | <code>null</code> |
| [tag_bindings](variables.tf#L188) | Tag bindings for this folder, in key => tag value id format. | <code>map&#40;string&#41;</code> | | <code>null</code> |
## Outputs ## Outputs

View File

@ -64,6 +64,13 @@ resource "google_folder_iam_member" "additive" {
member = each.value.member member = each.value.member
} }
resource "google_folder_iam_member" "members" {
for_each = var.iam_members
folder = local.folder.name
role = each.value.role
member = each.value.member
}
resource "google_folder_iam_policy" "authoritative" { resource "google_folder_iam_policy" "authoritative" {
count = var.iam_policy != null ? 1 : 0 count = var.iam_policy != null ? 1 : 0
folder = local.folder.name folder = local.folder.name

View File

@ -62,6 +62,16 @@ variable "iam_additive_members" {
nullable = false nullable = false
} }
variable "iam_members" {
description = "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."
type = map(object({
member = string
role = string
}))
nullable = false
default = {}
}
variable "iam_policy" { variable "iam_policy" {
description = "IAM authoritative policy in {ROLE => [MEMBERS]} format. Roles and members not explicitly listed will be cleared, use with extreme caution." description = "IAM authoritative policy in {ROLE => [MEMBERS]} format. Roles and members not explicitly listed will be cleared, use with extreme caution."
type = map(list(string)) type = map(list(string))

View File

@ -27,7 +27,6 @@ module "myproject-default-service-accounts" {
``` ```
<!-- TFDOC OPTS files:1 --> <!-- TFDOC OPTS files:1 -->
<!-- BEGIN TFDOC --> <!-- BEGIN TFDOC -->
## Files ## Files
| name | description | resources | | name | description | resources |
@ -42,8 +41,8 @@ module "myproject-default-service-accounts" {
| name | description | type | required | default | | name | description | type | required | default |
|---|---|:---:|:---:|:---:| |---|---|:---:|:---:|:---:|
| [name](variables.tf#L91) | Name of the service account to create. | <code>string</code> | ✓ | | | [name](variables.tf#L101) | Name of the service account to create. | <code>string</code> | ✓ | |
| [project_id](variables.tf#L106) | Project id where service account will be created. | <code>string</code> | ✓ | | | [project_id](variables.tf#L116) | Project id where service account will be created. | <code>string</code> | ✓ | |
| [description](variables.tf#L17) | Optional description. | <code>string</code> | | <code>null</code> | | [description](variables.tf#L17) | Optional description. | <code>string</code> | | <code>null</code> |
| [display_name](variables.tf#L23) | Display name of the service account to create. | <code>string</code> | | <code>&#34;Terraform-managed.&#34;</code> | | [display_name](variables.tf#L23) | Display name of the service account to create. | <code>string</code> | | <code>&#34;Terraform-managed.&#34;</code> |
| [generate_key](variables.tf#L29) | Generate a key for service account. | <code>bool</code> | | <code>false</code> | | [generate_key](variables.tf#L29) | Generate a key for service account. | <code>bool</code> | | <code>false</code> |
@ -51,13 +50,14 @@ module "myproject-default-service-accounts" {
| [iam_additive](variables.tf#L42) | IAM additive bindings on the service account in {ROLE => [MEMBERS]} format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> | | [iam_additive](variables.tf#L42) | IAM additive bindings on the service account in {ROLE => [MEMBERS]} format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam_billing_roles](variables.tf#L49) | Billing account roles granted to this service account, by billing account id. Non-authoritative. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> | | [iam_billing_roles](variables.tf#L49) | Billing account roles granted to this service account, by billing account id. Non-authoritative. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam_folder_roles](variables.tf#L56) | Folder roles granted to this service account, by folder id. Non-authoritative. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> | | [iam_folder_roles](variables.tf#L56) | Folder roles granted to this service account, by folder id. Non-authoritative. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam_organization_roles](variables.tf#L63) | Organization roles granted to this service account, by organization id. Non-authoritative. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> | | [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. | <code title="map&#40;object&#40;&#123;&#10; member &#61; string&#10; role &#61; string&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam_project_roles](variables.tf#L70) | Project roles granted to this service account, by project id. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> | | [iam_organization_roles](variables.tf#L73) | Organization roles granted to this service account, by organization id. Non-authoritative. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam_sa_roles](variables.tf#L77) | Service account roles granted to this service account, by service account name. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> | | [iam_project_roles](variables.tf#L80) | Project roles granted to this service account, by project id. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam_storage_roles](variables.tf#L84) | Storage roles granted to this service account, by bucket name. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> | | [iam_sa_roles](variables.tf#L87) | Service account roles granted to this service account, by service account name. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [prefix](variables.tf#L96) | Prefix applied to service account names. | <code>string</code> | | <code>null</code> | | [iam_storage_roles](variables.tf#L94) | Storage roles granted to this service account, by bucket name. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [public_keys_directory](variables.tf#L111) | Path to public keys data files to upload to the service account (should have `.pem` extension). | <code>string</code> | | <code>&#34;&#34;</code> | | [prefix](variables.tf#L106) | Prefix applied to service account names. | <code>string</code> | | <code>null</code> |
| [service_account_create](variables.tf#L117) | Create service account. When set to false, uses a data source to reference an existing service account. | <code>bool</code> | | <code>true</code> | | [public_keys_directory](variables.tf#L121) | Path to public keys data files to upload to the service account (should have `.pem` extension). | <code>string</code> | | <code>&#34;&#34;</code> |
| [service_account_create](variables.tf#L127) | Create service account. When set to false, uses a data source to reference an existing service account. | <code>bool</code> | | <code>true</code> |
## Outputs ## Outputs
@ -70,5 +70,4 @@ module "myproject-default-service-accounts" {
| [name](outputs.tf#L48) | Service account name. | | | [name](outputs.tf#L48) | Service account name. | |
| [service_account](outputs.tf#L57) | Service account resource. | | | [service_account](outputs.tf#L57) | Service account resource. | |
| [service_account_credentials](outputs.tf#L62) | Service account json credential templates for uploaded public keys data. | | | [service_account_credentials](outputs.tf#L62) | Service account json credential templates for uploaded public keys data. | |
<!-- END TFDOC --> <!-- END TFDOC -->

View File

@ -134,6 +134,13 @@ resource "google_service_account_iam_member" "additive" {
member = local.resource_iam_email member = local.resource_iam_email
} }
resource "google_service_account_iam_member" "members" {
for_each = var.iam_members
service_account_id = each.value.entity
role = each.value.role
member = each.value.member
}
resource "google_storage_bucket_iam_member" "bucket-roles" { resource "google_storage_bucket_iam_member" "bucket-roles" {
for_each = { for_each = {
for pair in local.iam_storage_pairs : for pair in local.iam_storage_pairs :

View File

@ -60,6 +60,16 @@ variable "iam_folder_roles" {
nullable = false nullable = false
} }
variable "iam_members" {
description = "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."
type = map(object({
member = string
role = string
}))
nullable = false
default = {}
}
variable "iam_organization_roles" { variable "iam_organization_roles" {
description = "Organization roles granted to this service account, by organization id. Non-authoritative." description = "Organization roles granted to this service account, by organization id. Non-authoritative."
type = map(list(string)) type = map(list(string))

View File

@ -4,6 +4,16 @@ This module allows creating and managing KMS crypto keys and IAM bindings at bot
When using an existing keyring be mindful about applying IAM bindings, as all bindings used by this module are authoritative, and you might inadvertently override bindings managed by the keyring creator. When using an existing keyring be mindful about applying IAM bindings, as all bindings used by this module are authoritative, and you might inadvertently override bindings managed by the keyring creator.
<!-- BEGIN TOC -->
- [Protecting against destroy](#protecting-against-destroy)
- [Examples](#examples)
- [Using an existing keyring](#using-an-existing-keyring)
- [Keyring creation and crypto key rotation and IAM roles](#keyring-creation-and-crypto-key-rotation-and-iam-roles)
- [Crypto key purpose](#crypto-key-purpose)
- [Variables](#variables)
- [Outputs](#outputs)
<!-- END TOC -->
## Protecting against destroy ## Protecting against destroy
In this module **no lifecycle blocks are set on resources to prevent destroy**, in order to allow for experimentation and testing where rapid `apply`/`destroy` cycles are needed. If you plan on using this module to manage non-development resources, **clone it and uncomment the lifecycle blocks** found in `main.tf`. In this module **no lifecycle blocks are set on resources to prevent destroy**, in order to allow for experimentation and testing where rapid `apply`/`destroy` cycles are needed. If you plan on using this module to manage non-development resources, **clone it and uncomment the lifecycle blocks** found in `main.tf`.
@ -49,6 +59,13 @@ module "kms" {
] ]
} }
} }
key_iam_members = {
key-b-am1 = {
key = "key-b"
member = "user:am1@example.com"
role = "roles/cloudkms.cryptoKeyEncrypterDecrypter"
}
}
keyring = { location = "europe-west1", name = "test" } keyring = { location = "europe-west1", name = "test" }
keys = { keys = {
key-a = null key-a = null
@ -56,7 +73,7 @@ module "kms" {
key-c = { rotation_period = null, labels = { env = "test" } } key-c = { rotation_period = null, labels = { env = "test" } }
} }
} }
# tftest modules=1 resources=9 inventory=basic.yaml # tftest modules=1 resources=10
``` ```
### Crypto key purpose ### Crypto key purpose
@ -80,22 +97,23 @@ module "kms" {
# tftest modules=1 resources=4 # tftest modules=1 resources=4
``` ```
<!-- BEGIN TFDOC --> <!-- BEGIN TFDOC -->
## Variables ## Variables
| name | description | type | required | default | | name | description | type | required | default |
|---|---|:---:|:---:|:---:| |---|---|:---:|:---:|:---:|
| [keyring](variables.tf#L70) | Keyring attributes. | <code title="object&#40;&#123;&#10; location &#61; string&#10; name &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | | [keyring](variables.tf#L91) | Keyring attributes. | <code title="object&#40;&#123;&#10; location &#61; string&#10; name &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | |
| [project_id](variables.tf#L93) | Project id where the keyring will be created. | <code>string</code> | ✓ | | | [project_id](variables.tf#L114) | Project id where the keyring will be created. | <code>string</code> | ✓ | |
| [iam](variables.tf#L17) | Keyring IAM bindings in {ROLE => [MEMBERS]} format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> | | [iam](variables.tf#L17) | Keyring IAM bindings in {ROLE => [MEMBERS]} format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam_additive](variables.tf#L23) | Keyring IAM additive bindings in {ROLE => [MEMBERS]} format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> | | [iam_additive](variables.tf#L23) | Keyring IAM additive bindings in {ROLE => [MEMBERS]} format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [key_iam](variables.tf#L29) | Key IAM bindings in {KEY => {ROLE => [MEMBERS]}} format. | <code>map&#40;map&#40;list&#40;string&#41;&#41;&#41;</code> | | <code>&#123;&#125;</code> | | [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. | <code title="map&#40;object&#40;&#123;&#10; member &#61; string&#10; role &#61; string&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [key_iam_additive](variables.tf#L35) | Key IAM additive bindings in {ROLE => [MEMBERS]} format. | <code>map&#40;map&#40;list&#40;string&#41;&#41;&#41;</code> | | <code>&#123;&#125;</code> | | [key_iam](variables.tf#L39) | Key IAM bindings in {KEY => {ROLE => [MEMBERS]}} format. | <code>map&#40;map&#40;list&#40;string&#41;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [key_purpose](variables.tf#L41) | Per-key purpose, if not set defaults will be used. If purpose is not `ENCRYPT_DECRYPT` (the default), `version_template.algorithm` is required. | <code title="map&#40;object&#40;&#123;&#10; purpose &#61; string&#10; version_template &#61; object&#40;&#123;&#10; algorithm &#61; string&#10; protection_level &#61; string&#10; &#125;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> | | [key_iam_additive](variables.tf#L45) | Key IAM additive bindings in {KEY => {ROLE => [MEMBERS]}} format. | <code>map&#40;map&#40;list&#40;string&#41;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [key_purpose_defaults](variables.tf#L53) | 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. | <code title="object&#40;&#123;&#10; purpose &#61; string&#10; version_template &#61; object&#40;&#123;&#10; algorithm &#61; string&#10; protection_level &#61; string&#10; &#125;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; purpose &#61; null&#10; version_template &#61; null&#10;&#125;">&#123;&#8230;&#125;</code> | | [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. | <code title="map&#40;object&#40;&#123;&#10; key &#61; string&#10; member &#61; string&#10; role &#61; string&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [keyring_create](variables.tf#L78) | Set to false to manage keys and IAM bindings in an existing keyring. | <code>bool</code> | | <code>true</code> | | [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. | <code title="map&#40;object&#40;&#123;&#10; purpose &#61; string&#10; version_template &#61; object&#40;&#123;&#10; algorithm &#61; string&#10; protection_level &#61; string&#10; &#125;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [keys](variables.tf#L84) | Key names and base attributes. Set attributes to null if not needed. | <code title="map&#40;object&#40;&#123;&#10; rotation_period &#61; string&#10; labels &#61; map&#40;string&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> | | [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. | <code title="object&#40;&#123;&#10; purpose &#61; string&#10; version_template &#61; object&#40;&#123;&#10; algorithm &#61; string&#10; protection_level &#61; string&#10; &#125;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; purpose &#61; null&#10; version_template &#61; null&#10;&#125;">&#123;&#8230;&#125;</code> |
| [tag_bindings](variables.tf#L98) | Tag bindings for this keyring, in key => tag value id format. | <code>map&#40;string&#41;</code> | | <code>null</code> | | [keyring_create](variables.tf#L99) | Set to false to manage keys and IAM bindings in an existing keyring. | <code>bool</code> | | <code>true</code> |
| [keys](variables.tf#L105) | Key names and base attributes. Set attributes to null if not needed. | <code title="map&#40;object&#40;&#123;&#10; rotation_period &#61; string&#10; labels &#61; map&#40;string&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [tag_bindings](variables.tf#L119) | Tag bindings for this keyring, in key => tag value id format. | <code>map&#40;string&#41;</code> | | <code>null</code> |
## Outputs ## Outputs
@ -107,5 +125,4 @@ module "kms" {
| [keys](outputs.tf#L44) | Key resources. | | | [keys](outputs.tf#L44) | Key resources. | |
| [location](outputs.tf#L52) | Keyring location. | | | [location](outputs.tf#L52) | Keyring location. | |
| [name](outputs.tf#L60) | Keyring name. | | | [name](outputs.tf#L60) | Keyring name. | |
<!-- END TFDOC --> <!-- END TFDOC -->

97
modules/kms/iam.tf Normal file
View File

@ -0,0 +1,97 @@
/**
* 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
locals {
iam_additive_members = flatten([
for role, members in var.iam_additive : [
for member in members : {
member = member
role = role
}
]
])
key_iam_additive_members = flatten([
for key, roles in var.key_iam_additive : [
for role, members in roles : [
for member in members : {
key = key
member = member
role = role
}
]
]
])
key_iam_members = flatten([
for key, roles in var.key_iam : [
for role, members in roles : {
key = key
role = role
members = members
}
]
])
}
resource "google_kms_key_ring_iam_binding" "default" {
for_each = var.iam
key_ring_id = local.keyring.id
role = each.key
members = each.value
}
resource "google_kms_key_ring_iam_member" "default" {
for_each = {
for binding in local.iam_additive_members :
"${binding.role}${binding.member}" => binding
}
key_ring_id = local.keyring.id
role = each.value.role
member = each.value.member
}
resource "google_kms_key_ring_iam_member" "members" {
for_each = var.iam_members
key_ring_id = local.keyring.id
role = each.value.role
member = each.value.member
}
resource "google_kms_crypto_key_iam_binding" "default" {
for_each = {
for binding in local.key_iam_members :
"${binding.key}.${binding.role}" => binding
}
role = each.value.role
crypto_key_id = google_kms_crypto_key.default[each.value.key].id
members = each.value.members
}
resource "google_kms_crypto_key_iam_member" "default" {
for_each = {
for binding in local.key_iam_additive_members :
"${binding.key}.${binding.role}${binding.member}" => binding
}
role = each.value.role
crypto_key_id = google_kms_crypto_key.default[each.value.key].id
member = each.value.member
}
resource "google_kms_crypto_key_iam_member" "members" {
for_each = var.key_iam_members
crypto_key_id = google_kms_crypto_key.default[each.value.key].id
role = each.value.role
member = each.value.member
}

View File

@ -15,34 +15,6 @@
*/ */
locals { locals {
iam_additive_members = flatten([
for role, members in var.iam_additive : [
for member in members : {
member = member
role = role
}
]
])
key_iam_additive_members = flatten([
for key, roles in var.key_iam_additive : [
for role, members in roles : [
for member in members : {
key = key
member = member
role = role
}
]
]
])
key_iam_members = flatten([
for key, roles in var.key_iam : [
for role, members in roles : {
key = key
role = role
members = members
}
]
])
key_purpose = { key_purpose = {
for key, attrs in var.keys : key => try( for key, attrs in var.keys : key => try(
var.key_purpose[key], var.key_purpose_defaults var.key_purpose[key], var.key_purpose_defaults
@ -69,23 +41,6 @@ resource "google_kms_key_ring" "default" {
location = var.keyring.location location = var.keyring.location
} }
resource "google_kms_key_ring_iam_binding" "default" {
for_each = var.iam
key_ring_id = local.keyring.id
role = each.key
members = each.value
}
resource "google_kms_key_ring_iam_member" "default" {
for_each = {
for binding in local.iam_additive_members :
"${binding.role}${binding.member}" => binding
}
key_ring_id = local.keyring.id
role = each.value.role
member = each.value.member
}
resource "google_kms_crypto_key" "default" { resource "google_kms_crypto_key" "default" {
for_each = var.keys for_each = var.keys
key_ring = local.keyring.id key_ring = local.keyring.id
@ -101,23 +56,3 @@ resource "google_kms_crypto_key" "default" {
} }
} }
} }
resource "google_kms_crypto_key_iam_binding" "default" {
for_each = {
for binding in local.key_iam_members :
"${binding.key}.${binding.role}" => binding
}
role = each.value.role
crypto_key_id = google_kms_crypto_key.default[each.value.key].id
members = each.value.members
}
resource "google_kms_crypto_key_iam_member" "default" {
for_each = {
for binding in local.key_iam_additive_members :
"${binding.key}.${binding.role}${binding.member}" => binding
}
role = each.value.role
crypto_key_id = google_kms_crypto_key.default[each.value.key].id
member = each.value.member
}

View File

@ -26,6 +26,16 @@ variable "iam_additive" {
default = {} default = {}
} }
variable "iam_members" {
description = "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."
type = map(object({
member = string
role = string
}))
nullable = false
default = {}
}
variable "key_iam" { variable "key_iam" {
description = "Key IAM bindings in {KEY => {ROLE => [MEMBERS]}} format." description = "Key IAM bindings in {KEY => {ROLE => [MEMBERS]}} format."
type = map(map(list(string))) type = map(map(list(string)))
@ -33,11 +43,22 @@ variable "key_iam" {
} }
variable "key_iam_additive" { variable "key_iam_additive" {
description = "Key IAM additive bindings in {ROLE => [MEMBERS]} format." description = "Key IAM additive bindings in {KEY => {ROLE => [MEMBERS]}} format."
type = map(map(list(string))) type = map(map(list(string)))
default = {} default = {}
} }
variable "key_iam_members" {
description = "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."
type = map(object({
key = string
member = string
role = string
}))
nullable = false
default = {}
}
variable "key_purpose" { variable "key_purpose" {
description = "Per-key purpose, if not set defaults will be used. If purpose is not `ENCRYPT_DECRYPT` (the default), `version_template.algorithm` is required." description = "Per-key purpose, if not set defaults will be used. If purpose is not `ENCRYPT_DECRYPT` (the default), `version_template.algorithm` is required."
type = map(object({ type = map(object({

View File

@ -131,8 +131,15 @@ module "vpc" {
] ]
} }
} }
subnet_iam_members = {
subnet-2-am1 = {
member = "user:am1@example.com"
role = "roles/compute.networkUser"
subnet = "europe-west1/subnet-2"
} }
# tftest modules=1 resources=8 inventory=subnet-iam.yaml }
}
# tftest modules=1 resources=9 inventory=subnet-iam.yaml
``` ```
### Peering ### Peering
@ -534,10 +541,11 @@ module "vpc" {
| [shared_vpc_service_projects](variables.tf#L161) | Shared VPC service projects to register with this host. | <code>list&#40;string&#41;</code> | | <code>&#91;&#93;</code> | | [shared_vpc_service_projects](variables.tf#L161) | Shared VPC service projects to register with this host. | <code>list&#40;string&#41;</code> | | <code>&#91;&#93;</code> |
| [subnet_iam](variables.tf#L167) | Subnet IAM bindings in {REGION/NAME => {ROLE => [MEMBERS]} format. | <code>map&#40;map&#40;list&#40;string&#41;&#41;&#41;</code> | | <code>&#123;&#125;</code> | | [subnet_iam](variables.tf#L167) | Subnet IAM bindings in {REGION/NAME => {ROLE => [MEMBERS]} format. | <code>map&#40;map&#40;list&#40;string&#41;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [subnet_iam_additive](variables.tf#L173) | Subnet IAM additive bindings in {REGION/NAME => {ROLE => [MEMBERS]}} format. | <code>map&#40;map&#40;list&#40;string&#41;&#41;&#41;</code> | | <code>&#123;&#125;</code> | | [subnet_iam_additive](variables.tf#L173) | Subnet IAM additive bindings in {REGION/NAME => {ROLE => [MEMBERS]}} format. | <code>map&#40;map&#40;list&#40;string&#41;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [subnets](variables.tf#L180) | Subnet configuration. | <code title="list&#40;object&#40;&#123;&#10; name &#61; string&#10; ip_cidr_range &#61; string&#10; region &#61; string&#10; description &#61; optional&#40;string&#41;&#10; enable_private_access &#61; optional&#40;bool, true&#41;&#10; flow_logs_config &#61; optional&#40;object&#40;&#123;&#10; aggregation_interval &#61; optional&#40;string&#41;&#10; filter_expression &#61; optional&#40;string&#41;&#10; flow_sampling &#61; optional&#40;number&#41;&#10; metadata &#61; optional&#40;string&#41;&#10; metadata_fields &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#41;&#10; ipv6 &#61; optional&#40;object&#40;&#123;&#10; access_type &#61; optional&#40;string, &#34;INTERNAL&#34;&#41;&#10; &#125;&#41;&#41;&#10; secondary_ip_ranges &#61; optional&#40;map&#40;string&#41;&#41;&#10;&#125;&#41;&#41;">list&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#91;&#93;</code> | | [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. | <code title="map&#40;object&#40;&#123;&#10; member &#61; string&#10; role &#61; string&#10; subnet &#61; string&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [subnets_proxy_only](variables.tf#L206) | 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. | <code title="list&#40;object&#40;&#123;&#10; name &#61; string&#10; ip_cidr_range &#61; string&#10; region &#61; string&#10; description &#61; optional&#40;string&#41;&#10; active &#61; bool&#10;&#125;&#41;&#41;">list&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#91;&#93;</code> | | [subnets](variables.tf#L191) | Subnet configuration. | <code title="list&#40;object&#40;&#123;&#10; name &#61; string&#10; ip_cidr_range &#61; string&#10; region &#61; string&#10; description &#61; optional&#40;string&#41;&#10; enable_private_access &#61; optional&#40;bool, true&#41;&#10; flow_logs_config &#61; optional&#40;object&#40;&#123;&#10; aggregation_interval &#61; optional&#40;string&#41;&#10; filter_expression &#61; optional&#40;string&#41;&#10; flow_sampling &#61; optional&#40;number&#41;&#10; metadata &#61; optional&#40;string&#41;&#10; metadata_fields &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#41;&#10; ipv6 &#61; optional&#40;object&#40;&#123;&#10; access_type &#61; optional&#40;string, &#34;INTERNAL&#34;&#41;&#10; &#125;&#41;&#41;&#10; secondary_ip_ranges &#61; optional&#40;map&#40;string&#41;&#41;&#10;&#125;&#41;&#41;">list&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#91;&#93;</code> |
| [subnets_psc](variables.tf#L218) | List of subnets for Private Service Connect service producers. | <code title="list&#40;object&#40;&#123;&#10; name &#61; string&#10; ip_cidr_range &#61; string&#10; region &#61; string&#10; description &#61; optional&#40;string&#41;&#10;&#125;&#41;&#41;">list&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#91;&#93;</code> | | [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. | <code title="list&#40;object&#40;&#123;&#10; name &#61; string&#10; ip_cidr_range &#61; string&#10; region &#61; string&#10; description &#61; optional&#40;string&#41;&#10; active &#61; bool&#10;&#125;&#41;&#41;">list&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#91;&#93;</code> |
| [vpc_create](variables.tf#L229) | Create VPC. When set to false, uses a data source to reference existing VPC. | <code>bool</code> | | <code>true</code> | | [subnets_psc](variables.tf#L229) | List of subnets for Private Service Connect service producers. | <code title="list&#40;object&#40;&#123;&#10; name &#61; string&#10; ip_cidr_range &#61; string&#10; region &#61; string&#10; description &#61; optional&#40;string&#41;&#10;&#125;&#41;&#41;">list&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#91;&#93;</code> |
| [vpc_create](variables.tf#L240) | Create VPC. When set to false, uses a data source to reference existing VPC. | <code>bool</code> | | <code>true</code> |
## Outputs ## Outputs

View File

@ -189,3 +189,12 @@ resource "google_compute_subnetwork_iam_member" "binding" {
role = each.value.role role = each.value.role
member = each.value.member member = each.value.member
} }
resource "google_compute_subnetwork_iam_member" "members" {
for_each = var.subnet_iam_members
project = var.project_id
subnetwork = google_compute_subnetwork.subnetwork[each.value.subnet].name
region = google_compute_subnetwork.subnetwork[each.value.subnet].region
role = each.value.role
member = each.value.member
}

View File

@ -177,6 +177,17 @@ variable "subnet_iam_additive" {
nullable = false nullable = false
} }
variable "subnet_iam_members" {
description = "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."
type = map(object({
member = string
role = string
subnet = string
}))
nullable = false
default = {}
}
variable "subnets" { variable "subnets" {
description = "Subnet configuration." description = "Subnet configuration."
type = list(object({ type = list(object({

View File

@ -45,6 +45,12 @@ module "org" {
iam_additive_members = { iam_additive_members = {
"user:compute@example.org" = ["roles/compute.admin", "roles/container.viewer"] "user:compute@example.org" = ["roles/compute.admin", "roles/container.viewer"]
} }
iam_members = {
am1-storage-admin = {
member = "user:am1@example.org"
role = "roles/storage.admin"
}
}
tags = { tags = {
allowexternal = { allowexternal = {
description = "Allow external identities." description = "Allow external identities."
@ -115,14 +121,14 @@ module "org" {
} }
} }
} }
# tftest modules=1 resources=16 inventory=basic.yaml # tftest modules=1 resources=17 inventory=basic.yaml
``` ```
## IAM ## IAM
There are three mutually exclusive ways of managing IAM in this module There are three mutually exclusive ways at the role level of managing IAM in this module
- non-authoritative via the `iam_additive` and `iam_additive_members` variables, where bindings created outside this module will coexist with those managed here - non-authoritative via the `iam_additive`, `iam_additive_members` and `iam_members` variables, where bindings created outside this module will coexist with those managed here
- authoritative via the `group_iam` and `iam` variables, where bindings created outside this module (eg in the console) will be removed at each `terraform apply` cycle if the same role is also managed here - authoritative via the `group_iam` and `iam` variables, where bindings created outside this module (eg in the console) will be removed at each `terraform apply` cycle if the same role is also managed here
- authoritative policy via the `iam_policy` variable, where any binding created outside this module (eg in the console) will be removed at each `terraform apply` cycle regardless of the role - authoritative policy via the `iam_policy` variable, where any binding created outside this module (eg in the console) will be removed at each `terraform apply` cycle regardless of the role
@ -469,7 +475,7 @@ module "org" {
| name | description | type | required | default | | name | description | type | required | default |
|---|---|:---:|:---:|:---:| |---|---|:---:|:---:|:---:|
| [organization_id](variables.tf#L199) | Organization id in organizations/nnnnnn format. | <code>string</code> | ✓ | | | [organization_id](variables.tf#L209) | Organization id in organizations/nnnnnn format. | <code>string</code> | ✓ | |
| [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. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> | | [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. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [custom_roles](variables.tf#L24) | Map of role name => list of permissions to create in this project. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> | | [custom_roles](variables.tf#L24) | Map of role name => list of permissions to create in this project. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [firewall_policy_associations](variables.tf#L31) | Hierarchical firewall policies to associate to this folder, in association name => policy id format. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> | | [firewall_policy_associations](variables.tf#L31) | Hierarchical firewall policies to associate to this folder, in association name => policy id format. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> |
@ -477,17 +483,18 @@ module "org" {
| [iam](variables.tf#L45) | IAM bindings, in {ROLE => [MEMBERS]} format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> | | [iam](variables.tf#L45) | IAM bindings, in {ROLE => [MEMBERS]} format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam_additive](variables.tf#L52) | Non authoritative IAM bindings, in {ROLE => [MEMBERS]} format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> | | [iam_additive](variables.tf#L52) | Non authoritative IAM bindings, in {ROLE => [MEMBERS]} format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam_additive_members](variables.tf#L59) | IAM additive bindings in {MEMBERS => [ROLE]} format. This might break if members are dynamic values. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> | | [iam_additive_members](variables.tf#L59) | IAM additive bindings in {MEMBERS => [ROLE]} format. This might break if members are dynamic values. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam_policy](variables.tf#L66) | IAM authoritative policy in {ROLE => [MEMBERS]} format. Roles and members not explicitly listed will be cleared, use with extreme caution. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>null</code> | | [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. | <code title="map&#40;object&#40;&#123;&#10; member &#61; string&#10; role &#61; string&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [logging_data_access](variables.tf#L72) | Control activation of data access logs. Format is service => { log type => [exempted members]}. The special 'allServices' key denotes configuration for all services. | <code>map&#40;map&#40;list&#40;string&#41;&#41;&#41;</code> | | <code>&#123;&#125;</code> | | [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. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>null</code> |
| [logging_exclusions](variables.tf#L87) | Logging exclusions for this organization in the form {NAME -> FILTER}. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> | | [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. | <code>map&#40;map&#40;list&#40;string&#41;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [logging_sinks](variables.tf#L94) | Logging sinks to create for the organization. | <code title="map&#40;object&#40;&#123;&#10; bq_partitioned_table &#61; optional&#40;bool&#41;&#10; description &#61; optional&#40;string&#41;&#10; destination &#61; string&#10; disabled &#61; optional&#40;bool, false&#41;&#10; exclusions &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; filter &#61; string&#10; include_children &#61; optional&#40;bool, true&#41;&#10; type &#61; string&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> | | [logging_exclusions](variables.tf#L97) | Logging exclusions for this organization in the form {NAME -> FILTER}. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> |
| [network_tags](variables.tf#L124) | 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. | <code title="map&#40;object&#40;&#123;&#10; description &#61; optional&#40;string, &#34;Managed by the Terraform organization module.&#34;&#41;&#10; iam &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; id &#61; optional&#40;string&#41;&#10; network &#61; string &#35; project_id&#47;vpc_name&#10; values &#61; optional&#40;map&#40;object&#40;&#123;&#10; description &#61; optional&#40;string, &#34;Managed by the Terraform organization module.&#34;&#41;&#10; iam &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; &#125;&#41;&#41;, &#123;&#125;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> | | [logging_sinks](variables.tf#L104) | Logging sinks to create for the organization. | <code title="map&#40;object&#40;&#123;&#10; bq_partitioned_table &#61; optional&#40;bool&#41;&#10; description &#61; optional&#40;string&#41;&#10; destination &#61; string&#10; disabled &#61; optional&#40;bool, false&#41;&#10; exclusions &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; filter &#61; string&#10; include_children &#61; optional&#40;bool, true&#41;&#10; type &#61; string&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [org_policies](variables.tf#L146) | Organization policies applied to this organization keyed by policy name. | <code title="map&#40;object&#40;&#123;&#10; inherit_from_parent &#61; optional&#40;bool&#41; &#35; for list policies only.&#10; reset &#61; optional&#40;bool&#41;&#10; rules &#61; optional&#40;list&#40;object&#40;&#123;&#10; allow &#61; optional&#40;object&#40;&#123;&#10; all &#61; optional&#40;bool&#41;&#10; values &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#41;&#10; deny &#61; optional&#40;object&#40;&#123;&#10; all &#61; optional&#40;bool&#41;&#10; values &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#41;&#10; enforce &#61; optional&#40;bool&#41; &#35; for boolean policies only.&#10; condition &#61; optional&#40;object&#40;&#123;&#10; description &#61; optional&#40;string&#41;&#10; expression &#61; optional&#40;string&#41;&#10; location &#61; optional&#40;string&#41;&#10; title &#61; optional&#40;string&#41;&#10; &#125;&#41;, &#123;&#125;&#41;&#10; &#125;&#41;&#41;, &#91;&#93;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> | | [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. | <code title="map&#40;object&#40;&#123;&#10; description &#61; optional&#40;string, &#34;Managed by the Terraform organization module.&#34;&#41;&#10; iam &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; id &#61; optional&#40;string&#41;&#10; network &#61; string &#35; project_id&#47;vpc_name&#10; values &#61; optional&#40;map&#40;object&#40;&#123;&#10; description &#61; optional&#40;string, &#34;Managed by the Terraform organization module.&#34;&#41;&#10; iam &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; &#125;&#41;&#41;, &#123;&#125;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [org_policies_data_path](variables.tf#L173) | Path containing org policies in YAML format. | <code>string</code> | | <code>null</code> | | [org_policies](variables.tf#L156) | Organization policies applied to this organization keyed by policy name. | <code title="map&#40;object&#40;&#123;&#10; inherit_from_parent &#61; optional&#40;bool&#41; &#35; for list policies only.&#10; reset &#61; optional&#40;bool&#41;&#10; rules &#61; optional&#40;list&#40;object&#40;&#123;&#10; allow &#61; optional&#40;object&#40;&#123;&#10; all &#61; optional&#40;bool&#41;&#10; values &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#41;&#10; deny &#61; optional&#40;object&#40;&#123;&#10; all &#61; optional&#40;bool&#41;&#10; values &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#41;&#10; enforce &#61; optional&#40;bool&#41; &#35; for boolean policies only.&#10; condition &#61; optional&#40;object&#40;&#123;&#10; description &#61; optional&#40;string&#41;&#10; expression &#61; optional&#40;string&#41;&#10; location &#61; optional&#40;string&#41;&#10; title &#61; optional&#40;string&#41;&#10; &#125;&#41;, &#123;&#125;&#41;&#10; &#125;&#41;&#41;, &#91;&#93;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [org_policy_custom_constraints](variables.tf#L179) | Organization policy custom constraints keyed by constraint name. | <code title="map&#40;object&#40;&#123;&#10; display_name &#61; optional&#40;string&#41;&#10; description &#61; optional&#40;string&#41;&#10; action_type &#61; string&#10; condition &#61; string&#10; method_types &#61; list&#40;string&#41;&#10; resource_types &#61; list&#40;string&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> | | [org_policies_data_path](variables.tf#L183) | Path containing org policies in YAML format. | <code>string</code> | | <code>null</code> |
| [org_policy_custom_constraints_data_path](variables.tf#L193) | Path containing org policy custom constraints in YAML format. | <code>string</code> | | <code>null</code> | | [org_policy_custom_constraints](variables.tf#L189) | Organization policy custom constraints keyed by constraint name. | <code title="map&#40;object&#40;&#123;&#10; display_name &#61; optional&#40;string&#41;&#10; description &#61; optional&#40;string&#41;&#10; action_type &#61; string&#10; condition &#61; string&#10; method_types &#61; list&#40;string&#41;&#10; resource_types &#61; list&#40;string&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [tag_bindings](variables.tf#L208) | Tag bindings for this organization, in key => tag value id format. | <code>map&#40;string&#41;</code> | | <code>null</code> | | [org_policy_custom_constraints_data_path](variables.tf#L203) | Path containing org policy custom constraints in YAML format. | <code>string</code> | | <code>null</code> |
| [tags](variables.tf#L214) | 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. | <code title="map&#40;object&#40;&#123;&#10; description &#61; optional&#40;string, &#34;Managed by the Terraform organization module.&#34;&#41;&#10; iam &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; id &#61; optional&#40;string&#41;&#10; values &#61; optional&#40;map&#40;object&#40;&#123;&#10; description &#61; optional&#40;string, &#34;Managed by the Terraform organization module.&#34;&#41;&#10; iam &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; id &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;, &#123;&#125;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> | | [tag_bindings](variables.tf#L218) | Tag bindings for this organization, in key => tag value id format. | <code>map&#40;string&#41;</code> | | <code>null</code> |
| [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. | <code title="map&#40;object&#40;&#123;&#10; description &#61; optional&#40;string, &#34;Managed by the Terraform organization module.&#34;&#41;&#10; iam &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; id &#61; optional&#40;string&#41;&#10; values &#61; optional&#40;map&#40;object&#40;&#123;&#10; description &#61; optional&#40;string, &#34;Managed by the Terraform organization module.&#34;&#41;&#10; iam &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; id &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;, &#123;&#125;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
## Outputs ## Outputs

View File

@ -73,6 +73,13 @@ resource "google_organization_iam_member" "additive" {
member = each.value.member member = each.value.member
} }
resource "google_organization_iam_member" "members" {
for_each = var.iam_members
org_id = local.organization_id_numeric
role = each.value.role
member = each.value.member
}
resource "google_organization_iam_policy" "authoritative" { resource "google_organization_iam_policy" "authoritative" {
count = var.iam_policy != null ? 1 : 0 count = var.iam_policy != null ? 1 : 0
org_id = local.organization_id_numeric org_id = local.organization_id_numeric

View File

@ -63,6 +63,16 @@ variable "iam_additive_members" {
nullable = false nullable = false
} }
variable "iam_members" {
description = "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."
type = map(object({
member = string
role = string
}))
nullable = false
default = {}
}
variable "iam_policy" { variable "iam_policy" {
description = "IAM authoritative policy in {ROLE => [MEMBERS]} format. Roles and members not explicitly listed will be cleared, use with extreme caution." description = "IAM authoritative policy in {ROLE => [MEMBERS]} format. Roles and members not explicitly listed will be cleared, use with extreme caution."
type = map(list(string)) type = map(list(string))

View File

@ -10,7 +10,9 @@ This module implements the creation and management of one GCP project including
- [IAM](#iam) - [IAM](#iam)
- [Authoritative IAM](#authoritative-iam) - [Authoritative IAM](#authoritative-iam)
- [Additive IAM](#additive-iam) - [Additive IAM](#additive-iam)
- [Additive IAM by Member](#additive-iam-by-member) - [Additive IAM by Role](#additive-iam-by-role)
- [Additive IAM by Principal](#additive-iam-by-principal)
- [Additive IAM by Binding](#additive-iam-by-binding)
- [Service Identities and Authoritative IAM](#service-identities-and-authoritative-iam) - [Service Identities and Authoritative IAM](#service-identities-and-authoritative-iam)
- [Using Shortcodes for Service Identities in Additive Iam](#using-shortcodes-for-service-identities-in-additive-iam) - [Using Shortcodes for Service Identities in Additive Iam](#using-shortcodes-for-service-identities-in-additive-iam)
- [Service Identities Requiring Manual Iam Grants](#service-identities-requiring-manual-iam-grants) - [Service Identities Requiring Manual Iam Grants](#service-identities-requiring-manual-iam-grants)
@ -49,7 +51,7 @@ module "project" {
IAM is managed via several variables that implement different levels of control: IAM is managed via several variables that implement different levels of control:
- `group_iam` and `iam` configure authoritative bindings that manage individual roles exclusively, mapping to the [`google_project_iam_binding`](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/google_project_iam#google_project_iam_binding) resource - `group_iam` and `iam` configure authoritative bindings that manage individual roles exclusively, mapping to the [`google_project_iam_binding`](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/google_project_iam#google_project_iam_binding) resource
- `iam_additive` and `iam_additive_members` configure additive bindings that only manage individual role/member pairs, mapping to the [`google_project_iam_member`](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/google_project_iam#google_project_iam_member) resource - `iam_additive`, `iam_additive_members` and `iam_members` configure additive bindings that only manage individual role/member pairs, mapping to the [`google_project_iam_member`](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/google_project_iam#google_project_iam_member) resource
- `iam_policy` which controls the entire IAM policy for the project, where any binding created outside this module (eg in the console) will be removed at each `terraform apply` cycle regardless of the role - `iam_policy` which controls the entire IAM policy for the project, where any binding created outside this module (eg in the console) will be removed at each `terraform apply` cycle regardless of the role
The authoritative and additive approaches can be used together, provided different roles are managed by each. The IAM policy is incompatible with the other approaches, and must be used with extreme care. The authoritative and additive approaches can be used together, provided different roles are managed by each. The IAM policy is incompatible with the other approaches, and must be used with extreme care.
@ -109,6 +111,10 @@ module "project" {
Additive IAM is typically used where bindings for specific roles are controlled by different modules or in different Terraform stages. One example is when the project is created by one team but a different team manages service account creation for the project, and some of the project-level roles overlap in the two configurations. Additive IAM is typically used where bindings for specific roles are controlled by different modules or in different Terraform stages. One example is when the project is created by one team but a different team manages service account creation for the project, and some of the project-level roles overlap in the two configurations.
#### Additive IAM by Role
Additive IAM is supported via the `iam_additive` variable which is keyed by role:
```hcl ```hcl
module "project" { module "project" {
source = "./fabric/modules/project" source = "./fabric/modules/project"
@ -129,7 +135,9 @@ module "project" {
# tftest modules=1 resources=5 inventory=iam-additive.yaml # tftest modules=1 resources=5 inventory=iam-additive.yaml
``` ```
### Additive IAM by Member #### Additive IAM by Principal
Additive IAM is also supported via the `iam_additive_members` variable which is keyed by principal:
```hcl ```hcl
module "project" { module "project" {
@ -144,6 +152,33 @@ module "project" {
# tftest modules=1 resources=4 inventory=iam-additive-members.yaml # tftest modules=1 resources=4 inventory=iam-additive-members.yaml
``` ```
#### 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:
```hcl
module "project" {
source = "./fabric/modules/project"
name = "project-example"
iam_members = {
one-owner = {
member = "user:one@example.org"
role = "roles/owner"
}
two-viewer = {
member = "user:two@example.org"
role = "roles/viewer"
}
two-compute-admin = {
member = "user:two@example.org"
role = "roles/compute.admin"
}
}
}
# tftest modules=1 resources=4 inventory=iam-members.yaml
```
### Service Identities and Authoritative IAM ### Service Identities and Authoritative IAM
As mentioned above, there are cases where authoritative management of specific IAM roles results in removal of default bindings from service identities. One example is outlined below, with a simple workaround leveraging the `service_accounts` output to identify the service identity. A full list of service identities and their roles can be found [here](https://cloud.google.com/iam/docs/service-agents). As mentioned above, there are cases where authoritative management of specific IAM roles results in removal of default bindings from service identities. One example is outlined below, with a simple workaround leveraging the `service_accounts` output to identify the service identity. A full list of service identities and their roles can be found [here](https://cloud.google.com/iam/docs/service-agents).
@ -260,6 +295,7 @@ module "service-project" {
``` ```
The module allows also granting necessary permissions in host project to service identities by specifying which services will be used in service project in `grant_iam_for_services`. The module allows also granting necessary permissions in host project to service identities by specifying which services will be used in service project in `grant_iam_for_services`.
```hcl ```hcl
module "host-project" { module "host-project" {
source = "./fabric/modules/project" source = "./fabric/modules/project"
@ -622,7 +658,7 @@ output "compute_robot" {
| name | description | type | required | default | | name | description | type | required | default |
|---|---|:---:|:---:|:---:| |---|---|:---:|:---:|:---:|
| [name](variables.tf#L161) | Project name and id suffix. | <code>string</code> | ✓ | | | [name](variables.tf#L171) | Project name and id suffix. | <code>string</code> | ✓ | |
| [auto_create_network](variables.tf#L17) | Whether to create the default network for the project. | <code>bool</code> | | <code>false</code> | | [auto_create_network](variables.tf#L17) | Whether to create the default network for the project. | <code>bool</code> | | <code>false</code> |
| [billing_account](variables.tf#L23) | Billing account id. | <code>string</code> | | <code>null</code> | | [billing_account](variables.tf#L23) | Billing account id. | <code>string</code> | | <code>null</code> |
| [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. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> | | [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. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
@ -633,30 +669,31 @@ output "compute_robot" {
| [iam](variables.tf#L62) | IAM bindings in {ROLE => [MEMBERS]} format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> | | [iam](variables.tf#L62) | IAM bindings in {ROLE => [MEMBERS]} format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam_additive](variables.tf#L69) | IAM additive bindings in {ROLE => [MEMBERS]} format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> | | [iam_additive](variables.tf#L69) | IAM additive bindings in {ROLE => [MEMBERS]} format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam_additive_members](variables.tf#L76) | IAM additive bindings in {MEMBERS => [ROLE]} format. This might break if members are dynamic values. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> | | [iam_additive_members](variables.tf#L76) | IAM additive bindings in {MEMBERS => [ROLE]} format. This might break if members are dynamic values. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam_policy](variables.tf#L82) | IAM authoritative policy in {ROLE => [MEMBERS]} format. Roles and members not explicitly listed will be cleared, use with extreme caution. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>null</code> | | [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. | <code title="map&#40;object&#40;&#123;&#10; member &#61; string&#10; role &#61; string&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [labels](variables.tf#L88) | Resource labels. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> | | [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. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>null</code> |
| [lien_reason](variables.tf#L95) | If non-empty, creates a project lien with this description. | <code>string</code> | | <code>&#34;&#34;</code> | | [labels](variables.tf#L98) | Resource labels. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> |
| [logging_data_access](variables.tf#L101) | Control activation of data access logs. Format is service => { log type => [exempted members]}. The special 'allServices' key denotes configuration for all services. | <code>map&#40;map&#40;list&#40;string&#41;&#41;&#41;</code> | | <code>&#123;&#125;</code> | | [lien_reason](variables.tf#L105) | If non-empty, creates a project lien with this description. | <code>string</code> | | <code>&#34;&#34;</code> |
| [logging_exclusions](variables.tf#L116) | Logging exclusions for this project in the form {NAME -> FILTER}. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> | | [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. | <code>map&#40;map&#40;list&#40;string&#41;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [logging_sinks](variables.tf#L123) | Logging sinks to create for this project. | <code title="map&#40;object&#40;&#123;&#10; bq_partitioned_table &#61; optional&#40;bool&#41;&#10; description &#61; optional&#40;string&#41;&#10; destination &#61; string&#10; disabled &#61; optional&#40;bool, false&#41;&#10; exclusions &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; filter &#61; string&#10; iam &#61; optional&#40;bool, true&#41;&#10; type &#61; string&#10; unique_writer &#61; optional&#40;bool&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> | | [logging_exclusions](variables.tf#L126) | Logging exclusions for this project in the form {NAME -> FILTER}. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> |
| [metric_scopes](variables.tf#L154) | List of projects that will act as metric scopes for this project. | <code>list&#40;string&#41;</code> | | <code>&#91;&#93;</code> | | [logging_sinks](variables.tf#L133) | Logging sinks to create for this project. | <code title="map&#40;object&#40;&#123;&#10; bq_partitioned_table &#61; optional&#40;bool&#41;&#10; description &#61; optional&#40;string&#41;&#10; destination &#61; string&#10; disabled &#61; optional&#40;bool, false&#41;&#10; exclusions &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; filter &#61; string&#10; iam &#61; optional&#40;bool, true&#41;&#10; type &#61; string&#10; unique_writer &#61; optional&#40;bool&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [org_policies](variables.tf#L166) | Organization policies applied to this project keyed by policy name. | <code title="map&#40;object&#40;&#123;&#10; inherit_from_parent &#61; optional&#40;bool&#41; &#35; for list policies only.&#10; reset &#61; optional&#40;bool&#41;&#10; rules &#61; optional&#40;list&#40;object&#40;&#123;&#10; allow &#61; optional&#40;object&#40;&#123;&#10; all &#61; optional&#40;bool&#41;&#10; values &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#41;&#10; deny &#61; optional&#40;object&#40;&#123;&#10; all &#61; optional&#40;bool&#41;&#10; values &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#41;&#10; enforce &#61; optional&#40;bool&#41; &#35; for boolean policies only.&#10; condition &#61; optional&#40;object&#40;&#123;&#10; description &#61; optional&#40;string&#41;&#10; expression &#61; optional&#40;string&#41;&#10; location &#61; optional&#40;string&#41;&#10; title &#61; optional&#40;string&#41;&#10; &#125;&#41;, &#123;&#125;&#41;&#10; &#125;&#41;&#41;, &#91;&#93;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> | | [metric_scopes](variables.tf#L164) | List of projects that will act as metric scopes for this project. | <code>list&#40;string&#41;</code> | | <code>&#91;&#93;</code> |
| [org_policies_data_path](variables.tf#L193) | Path containing org policies in YAML format. | <code>string</code> | | <code>null</code> | | [org_policies](variables.tf#L176) | Organization policies applied to this project keyed by policy name. | <code title="map&#40;object&#40;&#123;&#10; inherit_from_parent &#61; optional&#40;bool&#41; &#35; for list policies only.&#10; reset &#61; optional&#40;bool&#41;&#10; rules &#61; optional&#40;list&#40;object&#40;&#123;&#10; allow &#61; optional&#40;object&#40;&#123;&#10; all &#61; optional&#40;bool&#41;&#10; values &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#41;&#10; deny &#61; optional&#40;object&#40;&#123;&#10; all &#61; optional&#40;bool&#41;&#10; values &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#41;&#10; enforce &#61; optional&#40;bool&#41; &#35; for boolean policies only.&#10; condition &#61; optional&#40;object&#40;&#123;&#10; description &#61; optional&#40;string&#41;&#10; expression &#61; optional&#40;string&#41;&#10; location &#61; optional&#40;string&#41;&#10; title &#61; optional&#40;string&#41;&#10; &#125;&#41;, &#123;&#125;&#41;&#10; &#125;&#41;&#41;, &#91;&#93;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [oslogin](variables.tf#L199) | Enable OS Login. | <code>bool</code> | | <code>false</code> | | [org_policies_data_path](variables.tf#L203) | Path containing org policies in YAML format. | <code>string</code> | | <code>null</code> |
| [oslogin_admins](variables.tf#L205) | List of IAM-style identities that will be granted roles necessary for OS Login administrators. | <code>list&#40;string&#41;</code> | | <code>&#91;&#93;</code> | | [oslogin](variables.tf#L209) | Enable OS Login. | <code>bool</code> | | <code>false</code> |
| [oslogin_users](variables.tf#L213) | List of IAM-style identities that will be granted roles necessary for OS Login users. | <code>list&#40;string&#41;</code> | | <code>&#91;&#93;</code> | | [oslogin_admins](variables.tf#L215) | List of IAM-style identities that will be granted roles necessary for OS Login administrators. | <code>list&#40;string&#41;</code> | | <code>&#91;&#93;</code> |
| [parent](variables.tf#L220) | Parent folder or organization in 'folders/folder_id' or 'organizations/org_id' format. | <code>string</code> | | <code>null</code> | | [oslogin_users](variables.tf#L223) | List of IAM-style identities that will be granted roles necessary for OS Login users. | <code>list&#40;string&#41;</code> | | <code>&#91;&#93;</code> |
| [prefix](variables.tf#L230) | Optional prefix used to generate project id and name. | <code>string</code> | | <code>null</code> | | [parent](variables.tf#L230) | Parent folder or organization in 'folders/folder_id' or 'organizations/org_id' format. | <code>string</code> | | <code>null</code> |
| [project_create](variables.tf#L240) | Create project. When set to false, uses a data source to reference existing project. | <code>bool</code> | | <code>true</code> | | [prefix](variables.tf#L240) | Optional prefix used to generate project id and name. | <code>string</code> | | <code>null</code> |
| [service_config](variables.tf#L246) | Configure service API activation. | <code title="object&#40;&#123;&#10; disable_on_destroy &#61; bool&#10; disable_dependent_services &#61; bool&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; disable_on_destroy &#61; false&#10; disable_dependent_services &#61; false&#10;&#125;">&#123;&#8230;&#125;</code> | | [project_create](variables.tf#L250) | Create project. When set to false, uses a data source to reference existing project. | <code>bool</code> | | <code>true</code> |
| [service_encryption_key_ids](variables.tf#L258) | Cloud KMS encryption key in {SERVICE => [KEY_URL]} format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> | | [service_config](variables.tf#L256) | Configure service API activation. | <code title="object&#40;&#123;&#10; disable_on_destroy &#61; bool&#10; disable_dependent_services &#61; bool&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; disable_on_destroy &#61; false&#10; disable_dependent_services &#61; false&#10;&#125;">&#123;&#8230;&#125;</code> |
| [service_perimeter_bridges](variables.tf#L265) | Name of VPC-SC Bridge perimeters to add project into. See comment in the variables file for format. | <code>list&#40;string&#41;</code> | | <code>null</code> | | [service_encryption_key_ids](variables.tf#L268) | Cloud KMS encryption key in {SERVICE => [KEY_URL]} format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [service_perimeter_standard](variables.tf#L272) | Name of VPC-SC Standard perimeter to add project into. See comment in the variables file for format. | <code>string</code> | | <code>null</code> | | [service_perimeter_bridges](variables.tf#L275) | Name of VPC-SC Bridge perimeters to add project into. See comment in the variables file for format. | <code>list&#40;string&#41;</code> | | <code>null</code> |
| [services](variables.tf#L278) | Service APIs to enable. | <code>list&#40;string&#41;</code> | | <code>&#91;&#93;</code> | | [service_perimeter_standard](variables.tf#L282) | Name of VPC-SC Standard perimeter to add project into. See comment in the variables file for format. | <code>string</code> | | <code>null</code> |
| [shared_vpc_host_config](variables.tf#L284) | Configures this project as a Shared VPC host project (mutually exclusive with shared_vpc_service_project). | <code title="object&#40;&#123;&#10; enabled &#61; bool&#10; service_projects &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | | [services](variables.tf#L288) | Service APIs to enable. | <code>list&#40;string&#41;</code> | | <code>&#91;&#93;</code> |
| [shared_vpc_service_config](variables.tf#L293) | Configures this project as a Shared VPC service project (mutually exclusive with shared_vpc_host_config). | <code title="object&#40;&#123;&#10; host_project &#61; string&#10; service_identity_iam &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; service_iam_grants &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; host_project &#61; null&#10;&#125;">&#123;&#8230;&#125;</code> | | [shared_vpc_host_config](variables.tf#L294) | Configures this project as a Shared VPC host project (mutually exclusive with shared_vpc_service_project). | <code title="object&#40;&#123;&#10; enabled &#61; bool&#10; service_projects &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [skip_delete](variables.tf#L315) | Allows the underlying resources to be destroyed without destroying the project itself. | <code>bool</code> | | <code>false</code> | | [shared_vpc_service_config](variables.tf#L303) | Configures this project as a Shared VPC service project (mutually exclusive with shared_vpc_host_config). | <code title="object&#40;&#123;&#10; host_project &#61; string&#10; service_identity_iam &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; service_iam_grants &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; host_project &#61; null&#10;&#125;">&#123;&#8230;&#125;</code> |
| [tag_bindings](variables.tf#L321) | Tag bindings for this project, in key => tag value id format. | <code>map&#40;string&#41;</code> | | <code>null</code> | | [skip_delete](variables.tf#L325) | Allows the underlying resources to be destroyed without destroying the project itself. | <code>bool</code> | | <code>false</code> |
| [tag_bindings](variables.tf#L331) | Tag bindings for this project, in key => tag value id format. | <code>map&#40;string&#41;</code> | | <code>null</code> |
## Outputs ## Outputs

View File

@ -97,6 +97,17 @@ resource "google_project_iam_member" "additive" {
] ]
} }
resource "google_project_iam_member" "members" {
for_each = var.iam_members
project = local.project.project_id
role = each.value.role
member = each.value.member
depends_on = [
google_project_service.project_services,
google_project_iam_custom_role.roles
]
}
resource "google_project_iam_member" "oslogin_iam_serviceaccountuser" { resource "google_project_iam_member" "oslogin_iam_serviceaccountuser" {
for_each = var.oslogin ? toset(distinct(concat(var.oslogin_admins, var.oslogin_users))) : toset([]) for_each = var.oslogin ? toset(distinct(concat(var.oslogin_admins, var.oslogin_users))) : toset([])
project = local.project.project_id project = local.project.project_id

View File

@ -79,6 +79,16 @@ variable "iam_additive_members" {
default = {} default = {}
} }
variable "iam_members" {
description = "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."
type = map(object({
member = string
role = string
}))
nullable = false
default = {}
}
variable "iam_policy" { variable "iam_policy" {
description = "IAM authoritative policy in {ROLE => [MEMBERS]} format. Roles and members not explicitly listed will be cleared, use with extreme caution." description = "IAM authoritative policy in {ROLE => [MEMBERS]} format. Roles and members not explicitly listed will be cleared, use with extreme caution."
type = map(list(string)) type = map(list(string))

View File

@ -2,6 +2,15 @@
This module allows managing a single Cloud Source Repository, including IAM bindings and basic Cloud Build triggers. This module allows managing a single Cloud Source Repository, including IAM bindings and basic Cloud Build triggers.
<!-- BEGIN TOC -->
- [Examples](#examples)
- [Repository with IAM](#repository-with-iam)
- [Repository with Cloud Build trigger](#repository-with-cloud-build-trigger)
- [Files](#files)
- [Variables](#variables)
- [Outputs](#outputs)
<!-- END TOC -->
## Examples ## Examples
### Repository with IAM ### Repository with IAM
@ -14,8 +23,14 @@ module "repo" {
iam = { iam = {
"roles/source.reader" = ["user:foo@example.com"] "roles/source.reader" = ["user:foo@example.com"]
} }
iam_members = {
am1-reader = {
member = "user:am1@example.com"
role = "roles/source.reader"
} }
# tftest modules=1 resources=2 inventory=simple.yaml }
}
# tftest modules=1 resources=3 inventory=simple.yaml
``` ```
### Repository with Cloud Build trigger ### Repository with Cloud Build trigger
@ -46,7 +61,6 @@ module "repo" {
<!-- TFDOC OPTS files:1 --> <!-- TFDOC OPTS files:1 -->
<!-- BEGIN TFDOC --> <!-- BEGIN TFDOC -->
## Files ## Files
| name | description | resources | | name | description | resources |
@ -61,13 +75,14 @@ module "repo" {
| name | description | type | required | default | | name | description | type | required | default |
|---|---|:---:|:---:|:---:| |---|---|:---:|:---:|:---:|
| [name](variables.tf#L44) | Repository name. | <code>string</code> | ✓ | | | [name](variables.tf#L54) | Repository name. | <code>string</code> | ✓ | |
| [project_id](variables.tf#L49) | Project used for resources. | <code>string</code> | ✓ | | | [project_id](variables.tf#L59) | Project used for resources. | <code>string</code> | ✓ | |
| [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. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> | | [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. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam](variables.tf#L24) | IAM bindings in {ROLE => [MEMBERS]} format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> | | [iam](variables.tf#L24) | IAM bindings in {ROLE => [MEMBERS]} format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam_additive](variables.tf#L31) | IAM additive bindings in {ROLE => [MEMBERS]} format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> | | [iam_additive](variables.tf#L31) | IAM additive bindings in {ROLE => [MEMBERS]} format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam_additive_members](variables.tf#L38) | IAM additive bindings in {MEMBERS => [ROLE]} format. This might break if members are dynamic values. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> | | [iam_additive_members](variables.tf#L38) | IAM additive bindings in {MEMBERS => [ROLE]} format. This might break if members are dynamic values. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [triggers](variables.tf#L54) | Cloud Build triggers. | <code title="map&#40;object&#40;&#123;&#10; filename &#61; string&#10; included_files &#61; list&#40;string&#41;&#10; service_account &#61; string&#10; substitutions &#61; map&#40;string&#41;&#10; template &#61; object&#40;&#123;&#10; branch_name &#61; string&#10; project_id &#61; string&#10; tag_name &#61; string&#10; &#125;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> | | [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. | <code title="map&#40;object&#40;&#123;&#10; member &#61; string&#10; role &#61; string&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [triggers](variables.tf#L64) | Cloud Build triggers. | <code title="map&#40;object&#40;&#123;&#10; filename &#61; string&#10; included_files &#61; list&#40;string&#41;&#10; service_account &#61; string&#10; substitutions &#61; map&#40;string&#41;&#10; template &#61; object&#40;&#123;&#10; branch_name &#61; string&#10; project_id &#61; string&#10; tag_name &#61; string&#10; &#125;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
## Outputs ## Outputs
@ -76,5 +91,4 @@ module "repo" {
| [id](outputs.tf#L17) | Fully qualified repository id. | | | [id](outputs.tf#L17) | Fully qualified repository id. | |
| [name](outputs.tf#L22) | Repository name. | | | [name](outputs.tf#L22) | Repository name. | |
| [url](outputs.tf#L27) | Repository URL. | | | [url](outputs.tf#L27) | Repository URL. | |
<!-- END TFDOC --> <!-- END TFDOC -->

View File

@ -65,3 +65,11 @@ resource "google_sourcerepo_repository_iam_member" "additive" {
role = each.value.role role = each.value.role
member = each.value.member member = each.value.member
} }
resource "google_sourcerepo_repository_iam_member" "members" {
for_each = var.iam_members
project = var.project_id
repository = google_sourcerepo_repository.default.name
role = each.value.role
member = each.value.member
}

View File

@ -41,6 +41,16 @@ variable "iam_additive_members" {
default = {} default = {}
} }
variable "iam_members" {
description = "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."
type = map(object({
member = string
role = string
}))
nullable = false
default = {}
}
variable "name" { variable "name" {
description = "Repository name." description = "Repository name."
type = string type = string

View File

@ -11,6 +11,7 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
values: values:
module.dataplex-datascan.google_dataplex_datascan.datascan: module.dataplex-datascan.google_dataplex_datascan.datascan:
data: data:
@ -57,11 +58,19 @@ values:
- group:user-group@example.com - group:user-group@example.com
project: my-project-name project: my-project-name
role: roles/dataplex.dataScanViewer role: roles/dataplex.dataScanViewer
module.dataplex-datascan.google_dataplex_datascan_iam_member.members["am1-viewer"]:
condition: []
data_scan_id: test-datascan
location: us-central1
member: user:am1@example.com
project: my-project-name
role: roles/dataplex.dataScanViewer
counts: counts:
google_dataplex_datascan: 1 google_dataplex_datascan: 1
google_dataplex_datascan_iam_binding: 3 google_dataplex_datascan_iam_binding: 3
google_dataplex_datascan_iam_member: 1
modules: 1 modules: 1
resources: 4 resources: 5
outputs: {} outputs: {}

View File

@ -16,6 +16,7 @@ values:
module.folder.google_folder.folder[0]: module.folder.google_folder.folder[0]:
display_name: Folder name display_name: Folder name
parent: organizations/1234567890 parent: organizations/1234567890
timeouts: null
module.folder.google_folder_iam_binding.authoritative["roles/owner"]: module.folder.google_folder_iam_binding.authoritative["roles/owner"]:
condition: [] condition: []
members: members:
@ -52,8 +53,16 @@ values:
condition: [] condition: []
member: user:am2@example.org member: user:am2@example.org
role: roles/storage.objectViewer role: roles/storage.objectViewer
module.folder.google_folder_iam_member.members["am1-storage-admin"]:
condition: []
member: user:am1@example.org
role: roles/storage.admin
counts: counts:
google_folder: 1 google_folder: 1
google_folder_iam_binding: 3 google_folder_iam_binding: 3
google_folder_iam_member: 5 google_folder_iam_member: 6
modules: 1
resources: 10
outputs: {}

View File

@ -19,12 +19,14 @@ values:
purpose: ENCRYPT_DECRYPT purpose: ENCRYPT_DECRYPT
rotation_period: null rotation_period: null
skip_initial_version_creation: null skip_initial_version_creation: null
timeouts: null
module.kms.google_kms_crypto_key.default["key-b"]: module.kms.google_kms_crypto_key.default["key-b"]:
labels: null labels: null
name: key-b name: key-b
purpose: ENCRYPT_DECRYPT purpose: ENCRYPT_DECRYPT
rotation_period: 604800s rotation_period: 604800s
skip_initial_version_creation: null skip_initial_version_creation: null
timeouts: null
module.kms.google_kms_crypto_key.default["key-c"]: module.kms.google_kms_crypto_key.default["key-c"]:
labels: labels:
env: test env: test
@ -32,23 +34,29 @@ values:
purpose: ENCRYPT_DECRYPT purpose: ENCRYPT_DECRYPT
rotation_period: null rotation_period: null
skip_initial_version_creation: null skip_initial_version_creation: null
timeouts: null
module.kms.google_kms_crypto_key_iam_binding.default["key-a.roles/cloudkms.admin"]: module.kms.google_kms_crypto_key_iam_binding.default["key-a.roles/cloudkms.admin"]:
condition: [] condition: []
members: members:
- user:user3@example.com - user:user3@example.com
role: roles/cloudkms.admin role: roles/cloudkms.admin
module.kms.google_kms_crypto_key_iam_member.default["key-b.roles/cloudkms.cryptoKeyEncrypterDecrypteruser:user4@example.com"]: ? module.kms.google_kms_crypto_key_iam_member.default["key-b.roles/cloudkms.cryptoKeyEncrypterDecrypteruser:user4@example.com"]
condition: [] : condition: []
member: user:user4@example.com member: user:user4@example.com
role: roles/cloudkms.cryptoKeyEncrypterDecrypter role: roles/cloudkms.cryptoKeyEncrypterDecrypter
module.kms.google_kms_crypto_key_iam_member.default["key-b.roles/cloudkms.cryptoKeyEncrypterDecrypteruser:user5@example.com"]: ? module.kms.google_kms_crypto_key_iam_member.default["key-b.roles/cloudkms.cryptoKeyEncrypterDecrypteruser:user5@example.com"]
condition: [] : condition: []
member: user:user5@example.com member: user:user5@example.com
role: roles/cloudkms.cryptoKeyEncrypterDecrypter role: roles/cloudkms.cryptoKeyEncrypterDecrypter
module.kms.google_kms_crypto_key_iam_member.members["key-b-am1"]:
condition: []
member: user:am1@example.com
role: roles/cloudkms.cryptoKeyEncrypterDecrypter
module.kms.google_kms_key_ring.default[0]: module.kms.google_kms_key_ring.default[0]:
location: europe-west1 location: europe-west1
name: test name: test
project: my-project project: my-project
timeouts: null
module.kms.google_kms_key_ring_iam_member.default["roles/cloudkms.cryptoKeyEncrypterDecrypteruser:user1@example.com"]: module.kms.google_kms_key_ring_iam_member.default["roles/cloudkms.cryptoKeyEncrypterDecrypteruser:user1@example.com"]:
condition: [] condition: []
member: user:user1@example.com member: user:user1@example.com
@ -61,6 +69,10 @@ values:
counts: counts:
google_kms_crypto_key: 3 google_kms_crypto_key: 3
google_kms_crypto_key_iam_binding: 1 google_kms_crypto_key_iam_binding: 1
google_kms_crypto_key_iam_member: 2 google_kms_crypto_key_iam_member: 3
google_kms_key_ring: 1 google_kms_key_ring: 1
google_kms_key_ring_iam_member: 2 google_kms_key_ring_iam_member: 2
modules: 1
resources: 10
outputs: {}

View File

@ -14,17 +14,63 @@
values: values:
module.vpc.google_compute_network.network[0]: module.vpc.google_compute_network.network[0]:
auto_create_subnetworks: false
delete_default_routes_on_create: false
description: Terraform-managed.
enable_ula_internal_ipv6: null
name: my-network name: my-network
network_firewall_policy_enforcement_order: AFTER_CLASSIC_FIREWALL
project: my-project project: my-project
routing_mode: GLOBAL
timeouts: null
module.vpc.google_compute_route.gateway["private-googleapis"]:
description: Terraform-managed.
dest_range: 199.36.153.8/30
name: my-network-private-googleapis
next_hop_gateway: default-internet-gateway
next_hop_ilb: null
next_hop_instance: null
next_hop_vpn_tunnel: null
priority: 1000
project: my-project
tags: null
timeouts: null
module.vpc.google_compute_route.gateway["restricted-googleapis"]:
description: Terraform-managed.
dest_range: 199.36.153.4/30
name: my-network-restricted-googleapis
next_hop_gateway: default-internet-gateway
next_hop_ilb: null
next_hop_instance: null
next_hop_vpn_tunnel: null
priority: 1000
project: my-project
tags: null
timeouts: null
module.vpc.google_compute_subnetwork.subnetwork["europe-west1/subnet-1"]: module.vpc.google_compute_subnetwork.subnetwork["europe-west1/subnet-1"]:
description: Terraform-managed.
ip_cidr_range: 10.0.1.0/24
ipv6_access_type: null
log_config: []
name: subnet-1 name: subnet-1
private_ip_google_access: true
project: my-project project: my-project
region: europe-west1 region: europe-west1
role: null
secondary_ip_range: []
timeouts: null
module.vpc.google_compute_subnetwork.subnetwork["europe-west1/subnet-2"]: module.vpc.google_compute_subnetwork.subnetwork["europe-west1/subnet-2"]:
description: Terraform-managed.
ip_cidr_range: 10.0.1.0/24
ipv6_access_type: null
log_config: []
name: subnet-2 name: subnet-2
private_ip_google_access: true private_ip_google_access: true
project: my-project project: my-project
region: europe-west1 region: europe-west1
role: null
secondary_ip_range: []
timeouts: null
module.vpc.google_compute_subnetwork_iam_binding.binding["europe-west1/subnet-1.roles/compute.networkUser"]: module.vpc.google_compute_subnetwork_iam_binding.binding["europe-west1/subnet-1.roles/compute.networkUser"]:
condition: [] condition: []
members: members:
@ -34,16 +80,23 @@ values:
region: europe-west1 region: europe-west1
role: roles/compute.networkUser role: roles/compute.networkUser
subnetwork: subnet-1 subnetwork: subnet-1
module.vpc.google_compute_subnetwork_iam_member.binding["europe-west1/subnet-2.roles/compute.networkUser.user:user2@example.com"]: ? module.vpc.google_compute_subnetwork_iam_member.binding["europe-west1/subnet-2.roles/compute.networkUser.group:group2@example.com"]
condition: [] : condition: []
member: group:group2@example.com
project: my-project
region: europe-west1
role: roles/compute.networkUser
subnetwork: subnet-2
? module.vpc.google_compute_subnetwork_iam_member.binding["europe-west1/subnet-2.roles/compute.networkUser.user:user2@example.com"]
: condition: []
member: user:user2@example.com member: user:user2@example.com
project: my-project project: my-project
region: europe-west1 region: europe-west1
role: roles/compute.networkUser role: roles/compute.networkUser
subnetwork: subnet-2 subnetwork: subnet-2
module.vpc.google_compute_subnetwork_iam_member.binding["europe-west1/subnet-2.roles/compute.networkUser.group:group2@example.com"]: module.vpc.google_compute_subnetwork_iam_member.members["subnet-2-am1"]:
condition: [] condition: []
member: group:group2@example.com member: user:am1@example.com
project: my-project project: my-project
region: europe-west1 region: europe-west1
role: roles/compute.networkUser role: roles/compute.networkUser
@ -51,7 +104,11 @@ values:
counts: counts:
google_compute_network: 1 google_compute_network: 1
google_compute_route: 2
google_compute_subnetwork: 2 google_compute_subnetwork: 2
google_compute_subnetwork_iam_binding: 1 google_compute_subnetwork_iam_binding: 1
google_compute_subnetwork_iam_member: 2 google_compute_subnetwork_iam_member: 3
google_compute_route: 2 modules: 1
resources: 9
outputs: {}

View File

@ -25,6 +25,7 @@ values:
deny_all: null deny_all: null
enforce: 'TRUE' enforce: 'TRUE'
values: [] values: []
timeouts: null
module.org.google_org_policy_policy.default["compute.skipDefaultNetworkCreation"]: module.org.google_org_policy_policy.default["compute.skipDefaultNetworkCreation"]:
name: organizations/1234567890/policies/compute.skipDefaultNetworkCreation name: organizations/1234567890/policies/compute.skipDefaultNetworkCreation
parent: organizations/1234567890 parent: organizations/1234567890
@ -37,6 +38,7 @@ values:
deny_all: null deny_all: null
enforce: 'TRUE' enforce: 'TRUE'
values: [] values: []
timeouts: null
module.org.google_org_policy_policy.default["compute.trustedImageProjects"]: module.org.google_org_policy_policy.default["compute.trustedImageProjects"]:
name: organizations/1234567890/policies/compute.trustedImageProjects name: organizations/1234567890/policies/compute.trustedImageProjects
parent: organizations/1234567890 parent: organizations/1234567890
@ -52,6 +54,7 @@ values:
- allowed_values: - allowed_values:
- projects/my-project - projects/my-project
denied_values: null denied_values: null
timeouts: null
module.org.google_org_policy_policy.default["compute.vmExternalIpAccess"]: module.org.google_org_policy_policy.default["compute.vmExternalIpAccess"]:
name: organizations/1234567890/policies/compute.vmExternalIpAccess name: organizations/1234567890/policies/compute.vmExternalIpAccess
parent: organizations/1234567890 parent: organizations/1234567890
@ -64,6 +67,20 @@ values:
deny_all: 'TRUE' deny_all: 'TRUE'
enforce: null enforce: null
values: [] values: []
timeouts: null
module.org.google_org_policy_policy.default["custom.gkeEnableAutoUpgrade"]:
name: organizations/1234567890/policies/custom.gkeEnableAutoUpgrade
parent: organizations/1234567890
spec:
- inherit_from_parent: null
reset: null
rules:
- allow_all: null
condition: []
deny_all: null
enforce: 'TRUE'
values: []
timeouts: null
module.org.google_org_policy_policy.default["iam.allowedPolicyMemberDomains"]: module.org.google_org_policy_policy.default["iam.allowedPolicyMemberDomains"]:
name: organizations/1234567890/policies/iam.allowedPolicyMemberDomains name: organizations/1234567890/policies/iam.allowedPolicyMemberDomains
parent: organizations/1234567890 parent: organizations/1234567890
@ -95,6 +112,7 @@ values:
- C0xxxxxxx - C0xxxxxxx
- C0yyyyyyy - C0yyyyyyy
denied_values: null denied_values: null
timeouts: null
module.org.google_org_policy_policy.default["iam.disableServiceAccountKeyCreation"]: module.org.google_org_policy_policy.default["iam.disableServiceAccountKeyCreation"]:
name: organizations/1234567890/policies/iam.disableServiceAccountKeyCreation name: organizations/1234567890/policies/iam.disableServiceAccountKeyCreation
parent: organizations/1234567890 parent: organizations/1234567890
@ -107,6 +125,7 @@ values:
deny_all: null deny_all: null
enforce: 'TRUE' enforce: 'TRUE'
values: [] values: []
timeouts: null
module.org.google_org_policy_policy.default["iam.disableServiceAccountKeyUpload"]: module.org.google_org_policy_policy.default["iam.disableServiceAccountKeyUpload"]:
name: organizations/1234567890/policies/iam.disableServiceAccountKeyUpload name: organizations/1234567890/policies/iam.disableServiceAccountKeyUpload
parent: organizations/1234567890 parent: organizations/1234567890
@ -128,6 +147,7 @@ values:
deny_all: null deny_all: null
enforce: 'FALSE' enforce: 'FALSE'
values: [] values: []
timeouts: null
module.org.google_organization_iam_binding.authoritative["roles/owner"]: module.org.google_organization_iam_binding.authoritative["roles/owner"]:
condition: [] condition: []
members: members:
@ -156,20 +176,34 @@ values:
member: user:compute@example.org member: user:compute@example.org
org_id: '1234567890' org_id: '1234567890'
role: roles/container.viewer role: roles/container.viewer
module.org.google_organization_iam_member.members["am1-storage-admin"]:
condition: []
member: user:am1@example.org
org_id: '1234567890'
role: roles/storage.admin
module.org.google_tags_tag_key.default["allowexternal"]: module.org.google_tags_tag_key.default["allowexternal"]:
description: Allow external identities. description: Allow external identities.
parent: organizations/1234567890 parent: organizations/1234567890
purpose: null purpose: null
purpose_data: null purpose_data: null
short_name: allowexternal short_name: allowexternal
timeouts: null
module.org.google_tags_tag_value.default["allowexternal/false"]: module.org.google_tags_tag_value.default["allowexternal/false"]:
description: Managed by the Terraform organization module.
short_name: 'false' short_name: 'false'
timeouts: null
module.org.google_tags_tag_value.default["allowexternal/true"]: module.org.google_tags_tag_value.default["allowexternal/true"]:
description: Managed by the Terraform organization module.
short_name: 'true' short_name: 'true'
timeouts: null
counts: counts:
google_org_policy_policy: 8 google_org_policy_policy: 8
google_organization_iam_binding: 3 google_organization_iam_binding: 3
google_organization_iam_member: 2 google_organization_iam_member: 3
google_tags_tag_key: 1 google_tags_tag_key: 1
google_tags_tag_value: 2 google_tags_tag_value: 2
modules: 1
resources: 17
outputs: {}

View File

@ -0,0 +1,48 @@
# 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.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
values:
module.project.google_project.project[0]:
auto_create_network: false
billing_account: null
folder_id: null
labels: null
name: project-example
org_id: null
project_id: project-example
skip_delete: false
timeouts: null
module.project.google_project_iam_member.members["one-owner"]:
condition: []
member: user:one@example.org
project: project-example
role: roles/owner
module.project.google_project_iam_member.members["two-compute-admin"]:
condition: []
member: user:two@example.org
project: project-example
role: roles/compute.admin
module.project.google_project_iam_member.members["two-viewer"]:
condition: []
member: user:two@example.org
project: project-example
role: roles/viewer
counts:
google_project: 1
google_project_iam_member: 3
modules: 1
resources: 4
outputs: {}

View File

@ -17,6 +17,7 @@ values:
name: my-repo name: my-repo
project: my-project project: my-project
pubsub_configs: [] pubsub_configs: []
timeouts: null
module.repo.google_sourcerepo_repository_iam_binding.authoritative["roles/source.reader"]: module.repo.google_sourcerepo_repository_iam_binding.authoritative["roles/source.reader"]:
condition: [] condition: []
members: members:
@ -24,7 +25,18 @@ values:
project: my-project project: my-project
repository: my-repo repository: my-repo
role: roles/source.reader role: roles/source.reader
module.repo.google_sourcerepo_repository_iam_member.members["am1-reader"]:
condition: []
member: user:am1@example.com
project: my-project
repository: my-repo
role: roles/source.reader
counts: counts:
google_sourcerepo_repository: 1 google_sourcerepo_repository: 1
google_sourcerepo_repository_iam_binding: 1 google_sourcerepo_repository_iam_binding: 1
google_sourcerepo_repository_iam_member: 1
modules: 1
resources: 3
outputs: {}