Support additional attributes for buckets/datasets in project factory module (#3755)

* extend attributes for project factory secondary resources

* remove extra files

* complete

* tf fmt

* tfdoc

* schemas

* fix tests

* tfdoc
This commit is contained in:
Ludovico Magnocavallo
2026-02-20 12:57:59 +01:00
committed by GitHub
parent db8eecc999
commit 67b1543e90
21 changed files with 657 additions and 91 deletions

View File

@@ -0,0 +1,10 @@
---
trigger: always_on
---
# Always make sure edited code passes linting checks
- run `tools/tfdoc.py` if variable or output definitions changed
- run `terraform fmt` on any edited Terraform file, and hcl examples in README files
- a schema change should be reflected in all the other places that use the same schema, those are documented in `tools/duplicate-diff.py`
- always make sure both example and module (or stage) level tests run for all the modules/stages where code was edited

View File

@@ -0,0 +1,18 @@
---
trigger: always_on
---
# Use factory datasets for resource configuration
**FAST stages should generally not implement factories, but leverage those defined in modules and their associated schemas.**
Stages in the FAST folder are split between Terraform code and datasets.
Code is used to implement and wire together "factories" that implement resource management, while the actual description of resources and their relationships is implemented via YAML configurations read by those factories.
- YAML configurations are grouped in "datasets" which implement a complete design for the stage
- each factory has a reference JSON schema used to describe and validate the YAML data
- factories are generally implemented in the underlying modules, not in FAST stages
- modules deal with one specific resource set (eg an instance and its disks, a project and its org policies, IAM, etc.) and generally implement a single factory
- the project and VPC factory modules are the exception, as they are designed as "macro modules" to support interrelated creation of resources pertaining to a larger scope
- modules that do not manage "sets" of resources (e.g. one project, one folder, one dataset, etc.) typically do not have an associated factory, or only do for sub-resources (e.g. rules in a single policy), those factories are either implemented in the "macro modules" or directly in FAST

View File

@@ -0,0 +1,9 @@
---
trigger: always_on
---
Modules are designed to be containers for all aspects related to uage of a resource type. The pattern is a module is focused on a specific resource (e.g. folder, project, vpc, etc.) and implements all the functionality needed to create/manage that resources so that it is ready for user consumption. This includes: IAM, sub resources (eg subnets and routes for a network), org policies where applicable, etc.
Unrelated resources like a dataset for a project should never be part of the same module, except in the two "aggregation modules" (project and vpc factory) where that makes sense to allow consumers to create baseline infrastructure ready to receive application-level resources.
Never, ever break this boundary as a first approach, and always, always check in with the user if this looks like the only plan of action, as the criteria and constraints that led to the plan might need to be revised instead.

View File

@@ -244,6 +244,39 @@
},
"encryption_key": {
"type": "string"
},
"iam": {
"$ref": "#/$defs/iam"
},
"iam_bindings": {
"$ref": "#/$defs/iam_bindings"
},
"iam_bindings_additive": {
"$ref": "#/$defs/iam_bindings_additive"
},
"iam_by_principals": {
"$ref": "#/$defs/iam_by_principals"
},
"options": {
"type": "object",
"additionalProperties": false,
"properties": {
"default_table_expiration_ms": {
"type": "number"
},
"default_partition_expiration_ms": {
"type": "number"
},
"delete_contents_on_destroy": {
"type": "boolean"
},
"max_time_travel_hours": {
"type": "number"
}
}
},
"tag_bindings": {
"$ref": "#/$defs/tag_bindings"
}
}
}
@@ -348,6 +381,9 @@
"iam_bindings_additive": {
"$ref": "#/$defs/iam_bindings_additive"
},
"tag_bindings": {
"$ref": "#/$defs/tag_bindings"
},
"keys": {
"type": "object",
"additionalProperties": false,
@@ -651,6 +687,9 @@
},
"iam_sa_roles": {
"$ref": "#/$defs/iam_sa_roles"
},
"tag_bindings": {
"$ref": "#/$defs/tag_bindings"
}
}
}
@@ -756,15 +795,6 @@
}
}
},
"tag_bindings": {
"type": "object",
"additionalProperties": false,
"patternProperties": {
"^[a-z0-9_-]+$": {
"type": "string"
}
}
},
"tags": {
"type": "object",
"additionalProperties": {
@@ -813,6 +843,15 @@
}
}
},
"tag_bindings": {
"type": "object",
"additionalProperties": false,
"patternProperties": {
"^[a-z0-9_-]+$": {
"type": "string"
}
}
},
"universe": {
"type": "object",
"additionalProperties": false,
@@ -1192,6 +1231,9 @@
},
"enable_object_retention": {
"type": "boolean"
},
"tag_bindings": {
"$ref": "#/$defs/tag_bindings"
}
}
},
@@ -1804,6 +1846,15 @@
}
}
}
},
"tag_bindings": {
"type": "object",
"additionalProperties": false,
"patternProperties": {
"^[a-z0-9_-]+$": {
"type": "string"
}
}
}
}
}

View File

@@ -244,6 +244,39 @@
},
"encryption_key": {
"type": "string"
},
"iam": {
"$ref": "#/$defs/iam"
},
"iam_bindings": {
"$ref": "#/$defs/iam_bindings"
},
"iam_bindings_additive": {
"$ref": "#/$defs/iam_bindings_additive"
},
"iam_by_principals": {
"$ref": "#/$defs/iam_by_principals"
},
"options": {
"type": "object",
"additionalProperties": false,
"properties": {
"default_table_expiration_ms": {
"type": "number"
},
"default_partition_expiration_ms": {
"type": "number"
},
"delete_contents_on_destroy": {
"type": "boolean"
},
"max_time_travel_hours": {
"type": "number"
}
}
},
"tag_bindings": {
"$ref": "#/$defs/tag_bindings"
}
}
}
@@ -348,6 +381,9 @@
"iam_bindings_additive": {
"$ref": "#/$defs/iam_bindings_additive"
},
"tag_bindings": {
"$ref": "#/$defs/tag_bindings"
},
"keys": {
"type": "object",
"additionalProperties": false,
@@ -651,6 +687,9 @@
},
"iam_sa_roles": {
"$ref": "#/$defs/iam_sa_roles"
},
"tag_bindings": {
"$ref": "#/$defs/tag_bindings"
}
}
}
@@ -756,15 +795,6 @@
}
}
},
"tag_bindings": {
"type": "object",
"additionalProperties": false,
"patternProperties": {
"^[a-z0-9_-]+$": {
"type": "string"
}
}
},
"tags": {
"type": "object",
"additionalProperties": {
@@ -813,6 +843,15 @@
}
}
},
"tag_bindings": {
"type": "object",
"additionalProperties": false,
"patternProperties": {
"^[a-z0-9_-]+$": {
"type": "string"
}
}
},
"universe": {
"type": "object",
"additionalProperties": false,
@@ -1192,6 +1231,9 @@
},
"enable_object_retention": {
"type": "boolean"
},
"tag_bindings": {
"$ref": "#/$defs/tag_bindings"
}
}
},
@@ -1804,6 +1846,15 @@
}
}
}
},
"tag_bindings": {
"type": "object",
"additionalProperties": false,
"patternProperties": {
"^[a-z0-9_-]+$": {
"type": "string"
}
}
}
}
}

View File

@@ -244,6 +244,39 @@
},
"encryption_key": {
"type": "string"
},
"iam": {
"$ref": "#/$defs/iam"
},
"iam_bindings": {
"$ref": "#/$defs/iam_bindings"
},
"iam_bindings_additive": {
"$ref": "#/$defs/iam_bindings_additive"
},
"iam_by_principals": {
"$ref": "#/$defs/iam_by_principals"
},
"options": {
"type": "object",
"additionalProperties": false,
"properties": {
"default_table_expiration_ms": {
"type": "number"
},
"default_partition_expiration_ms": {
"type": "number"
},
"delete_contents_on_destroy": {
"type": "boolean"
},
"max_time_travel_hours": {
"type": "number"
}
}
},
"tag_bindings": {
"$ref": "#/$defs/tag_bindings"
}
}
}
@@ -348,6 +381,9 @@
"iam_bindings_additive": {
"$ref": "#/$defs/iam_bindings_additive"
},
"tag_bindings": {
"$ref": "#/$defs/tag_bindings"
},
"keys": {
"type": "object",
"additionalProperties": false,
@@ -651,6 +687,9 @@
},
"iam_sa_roles": {
"$ref": "#/$defs/iam_sa_roles"
},
"tag_bindings": {
"$ref": "#/$defs/tag_bindings"
}
}
}
@@ -756,15 +795,6 @@
}
}
},
"tag_bindings": {
"type": "object",
"additionalProperties": false,
"patternProperties": {
"^[a-z0-9_-]+$": {
"type": "string"
}
}
},
"tags": {
"type": "object",
"additionalProperties": {
@@ -813,6 +843,15 @@
}
}
},
"tag_bindings": {
"type": "object",
"additionalProperties": false,
"patternProperties": {
"^[a-z0-9_-]+$": {
"type": "string"
}
}
},
"universe": {
"type": "object",
"additionalProperties": false,
@@ -1192,6 +1231,9 @@
},
"enable_object_retention": {
"type": "boolean"
},
"tag_bindings": {
"$ref": "#/$defs/tag_bindings"
}
}
},
@@ -1804,6 +1846,15 @@
}
}
}
},
"tag_bindings": {
"type": "object",
"additionalProperties": false,
"patternProperties": {
"^[a-z0-9_-]+$": {
"type": "string"
}
}
}
}
}

View File

@@ -244,6 +244,39 @@
},
"encryption_key": {
"type": "string"
},
"iam": {
"$ref": "#/$defs/iam"
},
"iam_bindings": {
"$ref": "#/$defs/iam_bindings"
},
"iam_bindings_additive": {
"$ref": "#/$defs/iam_bindings_additive"
},
"iam_by_principals": {
"$ref": "#/$defs/iam_by_principals"
},
"options": {
"type": "object",
"additionalProperties": false,
"properties": {
"default_table_expiration_ms": {
"type": "number"
},
"default_partition_expiration_ms": {
"type": "number"
},
"delete_contents_on_destroy": {
"type": "boolean"
},
"max_time_travel_hours": {
"type": "number"
}
}
},
"tag_bindings": {
"$ref": "#/$defs/tag_bindings"
}
}
}
@@ -348,6 +381,9 @@
"iam_bindings_additive": {
"$ref": "#/$defs/iam_bindings_additive"
},
"tag_bindings": {
"$ref": "#/$defs/tag_bindings"
},
"keys": {
"type": "object",
"additionalProperties": false,
@@ -651,6 +687,9 @@
},
"iam_sa_roles": {
"$ref": "#/$defs/iam_sa_roles"
},
"tag_bindings": {
"$ref": "#/$defs/tag_bindings"
}
}
}
@@ -756,15 +795,6 @@
}
}
},
"tag_bindings": {
"type": "object",
"additionalProperties": false,
"patternProperties": {
"^[a-z0-9_-]+$": {
"type": "string"
}
}
},
"tags": {
"type": "object",
"additionalProperties": {
@@ -813,6 +843,15 @@
}
}
},
"tag_bindings": {
"type": "object",
"additionalProperties": false,
"patternProperties": {
"^[a-z0-9_-]+$": {
"type": "string"
}
}
},
"universe": {
"type": "object",
"additionalProperties": false,
@@ -1192,6 +1231,9 @@
},
"enable_object_retention": {
"type": "boolean"
},
"tag_bindings": {
"$ref": "#/$defs/tag_bindings"
}
}
},
@@ -1804,6 +1846,15 @@
}
}
}
},
"tag_bindings": {
"type": "object",
"additionalProperties": false,
"patternProperties": {
"^[a-z0-9_-]+$": {
"type": "string"
}
}
}
}
}

View File

@@ -55,8 +55,14 @@ module "bigquery-dataset" {
iam = {
"roles/bigquery.dataOwner" = ["user:user1@example.org"]
}
iam_bindings = {
reader_user = {
role = "roles/bigquery.dataViewer"
members = ["user:user2@example.org"]
}
}
}
# tftest modules=1 resources=2 inventory=iam.yaml
# tftest modules=1 resources=3 inventory=iam.yaml
```
## Authorized Views, Datasets, and Routines
@@ -353,27 +359,30 @@ module "bigquery-dataset" {
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
| [id](variables.tf#L112) | Dataset id. | <code>string</code> | ✓ | |
| [project_id](variables.tf#L176) | Id of the project where datasets will be created. | <code>string</code> | ✓ | |
| [id](variables.tf#L111) | Dataset id. | <code>string</code> | ✓ | |
| [project_id](variables.tf#L175) | Id of the project where datasets will be created. | <code>string</code> | ✓ | |
| [access](variables.tf#L17) | Map of access rules with role and identity type. Keys are arbitrary and must match those in the `access_identities` variable, types are `domain`, `group`, `special_group`, `user`, `view`. | <code title="map&#40;object&#40;&#123;&#10; role &#61; string&#10; type &#61; string&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [access_identities](variables.tf#L33) | Map of access identities used for basic access roles. View identities have the format 'project_id\|dataset_id\|table_id'. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> |
| [authorized_datasets](variables.tf#L39) | An array of datasets to be authorized on the dataset. | <code title="list&#40;object&#40;&#123;&#10; dataset_id &#61; string,&#10; project_id &#61; string,&#10;&#125;&#41;&#41;">list&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#91;&#93;</code> |
| [authorized_routines](variables.tf#L48) | An array of routines to be authorized on the dataset. | <code title="list&#40;object&#40;&#123;&#10; project_id &#61; string,&#10; dataset_id &#61; string,&#10; routine_id &#61; string&#10;&#125;&#41;&#41;">list&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#91;&#93;</code> |
| [authorized_views](variables.tf#L58) | An array of views to be authorized on the dataset. | <code title="list&#40;object&#40;&#123;&#10; dataset_id &#61; string,&#10; project_id &#61; string,&#10; table_id &#61; string &#35; this is the view id, but we keep table_id to stay consistent as the resource&#10;&#125;&#41;&#41;">list&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#91;&#93;</code> |
| [context](variables.tf#L68) | Context-specific interpolations. | <code title="object&#40;&#123;&#10; custom_roles &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; kms_keys &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; iam_principals &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; locations &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; project_ids &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; tag_values &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> |
| [dataset_access](variables.tf#L82) | Set access in the dataset resource instead of using separate resources. | <code>bool</code> | | <code>false</code> |
| [description](variables.tf#L88) | Optional description. | <code>string</code> | | <code>&#34;Terraform managed.&#34;</code> |
| [encryption_key](variables.tf#L94) | Self link of the KMS key that will be used to protect destination table. | <code>string</code> | | <code>null</code> |
| [friendly_name](variables.tf#L100) | Dataset friendly name. | <code>string</code> | | <code>null</code> |
| [iam](variables.tf#L106) | IAM bindings in {ROLE => [MEMBERS]} format. Mutually exclusive with the access_* variables used for basic roles. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [labels](variables.tf#L117) | Dataset labels. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> |
| [location](variables.tf#L123) | Dataset location. | <code>string</code> | | <code>&#34;EU&#34;</code> |
| [materialized_views](variables.tf#L129) | Materialized views definitions. | <code title="map&#40;object&#40;&#123;&#10; query &#61; string&#10; allow_non_incremental_definition &#61; optional&#40;bool&#41;&#10; deletion_protection &#61; optional&#40;bool&#41;&#10; description &#61; optional&#40;string, &#34;Terraform managed.&#34;&#41;&#10; enable_refresh &#61; optional&#40;bool&#41;&#10; friendly_name &#61; optional&#40;string&#41;&#10; labels &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; refresh_interval_ms &#61; optional&#40;bool&#41;&#10; require_partition_filter &#61; optional&#40;bool&#41;&#10; options &#61; optional&#40;object&#40;&#123;&#10; clustering &#61; optional&#40;list&#40;string&#41;&#41;&#10; expiration_time &#61; optional&#40;number&#41;&#10; &#125;&#41;, &#123;&#125;&#41;&#10; partitioning &#61; optional&#40;object&#40;&#123;&#10; field &#61; optional&#40;string&#41;&#10; range &#61; optional&#40;object&#40;&#123;&#10; end &#61; number&#10; interval &#61; number&#10; start &#61; number&#10; &#125;&#41;&#41;&#10; time &#61; optional&#40;object&#40;&#123;&#10; type &#61; string&#10; expiration_ms &#61; optional&#40;number&#41;&#10; field &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [options](variables.tf#L162) | Dataset options. | <code title="object&#40;&#123;&#10; default_collation &#61; optional&#40;string&#41;&#10; default_table_expiration_ms &#61; optional&#40;number&#41;&#10; default_partition_expiration_ms &#61; optional&#40;number&#41;&#10; delete_contents_on_destroy &#61; optional&#40;bool, false&#41;&#10; is_case_insensitive &#61; optional&#40;bool&#41;&#10; max_time_travel_hours &#61; optional&#40;number, 168&#41;&#10; storage_billing_model &#61; optional&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> |
| [routines](variables.tf#L181) | Routine definitions. | <code title="map&#40;object&#40;&#123;&#10; description &#61; optional&#40;string&#41;&#10; routine_type &#61; string&#10; language &#61; optional&#40;string&#41;&#10; definition_body &#61; string&#10; imported_libraries &#61; optional&#40;list&#40;string&#41;&#41;&#10; determinism_level &#61; optional&#40;string&#41;&#10; data_governance_type &#61; optional&#40;string&#41;&#10; return_type &#61; optional&#40;string&#41;&#10; return_table_type &#61; optional&#40;string&#41;&#10; arguments &#61; optional&#40;map&#40;object&#40;&#123;&#10; argument_kind &#61; optional&#40;string&#41;&#10; mode &#61; optional&#40;string&#41;&#10; data_type &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;, &#123;&#125;&#41;&#10; spark_options &#61; optional&#40;object&#40;&#123;&#10; archive_uris &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; connection &#61; string&#10; container_image &#61; optional&#40;string&#41;&#10; file_uris &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; jar_uris &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; main_file_uri &#61; optional&#40;string&#41;&#10; main_class &#61; optional&#40;string&#41;&#10; properties &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; py_file_uris &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; runtime_version &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#10; remote_function_options &#61; optional&#40;object&#40;&#123;&#10; connection &#61; string&#10; endpoint &#61; optional&#40;string&#41;&#10; max_batching_rows &#61; optional&#40;string&#41;&#10; user_defined_context &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; &#125;&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [tables](variables.tf#L220) | Table definitions. Options and partitioning default to null. Partitioning can only use `range` or `time`, set the unused one to null. | <code title="map&#40;object&#40;&#123;&#10; deletion_protection &#61; optional&#40;bool&#41;&#10; description &#61; optional&#40;string, &#34;Terraform managed.&#34;&#41;&#10; friendly_name &#61; optional&#40;string&#41;&#10; labels &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; require_partition_filter &#61; optional&#40;bool&#41;&#10; schema &#61; optional&#40;string&#41;&#10; external_data_configuration &#61; optional&#40;object&#40;&#123;&#10; autodetect &#61; bool&#10; source_uris &#61; list&#40;string&#41;&#10; avro_logical_types &#61; optional&#40;bool&#41;&#10; compression &#61; optional&#40;string&#41;&#10; connection_id &#61; optional&#40;string&#41;&#10; file_set_spec_type &#61; optional&#40;string&#41;&#10; ignore_unknown_values &#61; optional&#40;bool&#41;&#10; metadata_cache_mode &#61; optional&#40;string&#41;&#10; object_metadata &#61; optional&#40;string&#41;&#10; json_options_encoding &#61; optional&#40;string&#41;&#10; reference_file_schema_uri &#61; optional&#40;string&#41;&#10; schema &#61; optional&#40;string&#41;&#10; source_format &#61; optional&#40;string&#41;&#10; max_bad_records &#61; optional&#40;number&#41;&#10; csv_options &#61; optional&#40;object&#40;&#123;&#10; quote &#61; string&#10; allow_jagged_rows &#61; optional&#40;bool&#41;&#10; allow_quoted_newlines &#61; optional&#40;bool&#41;&#10; encoding &#61; optional&#40;string&#41;&#10; field_delimiter &#61; optional&#40;string&#41;&#10; skip_leading_rows &#61; optional&#40;number&#41;&#10; &#125;&#41;&#41;&#10; google_sheets_options &#61; optional&#40;object&#40;&#123;&#10; range &#61; optional&#40;string&#41;&#10; skip_leading_rows &#61; optional&#40;number&#41;&#10; &#125;&#41;&#41;&#10; hive_partitioning_options &#61; optional&#40;object&#40;&#123;&#10; mode &#61; optional&#40;string&#41;&#10; require_partition_filter &#61; optional&#40;bool&#41;&#10; source_uri_prefix &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#10; parquet_options &#61; optional&#40;object&#40;&#123;&#10; enum_as_string &#61; optional&#40;bool&#41;&#10; enable_list_inference &#61; optional&#40;bool&#41;&#10; &#125;&#41;&#41;&#10;&#10;&#10; &#125;&#41;&#41;&#10; options &#61; optional&#40;object&#40;&#123;&#10; clustering &#61; optional&#40;list&#40;string&#41;&#41;&#10; encryption_key &#61; optional&#40;string&#41;&#10; expiration_time &#61; optional&#40;number&#41;&#10; max_staleness &#61; optional&#40;string&#41;&#10; &#125;&#41;, &#123;&#125;&#41;&#10; partitioning &#61; optional&#40;object&#40;&#123;&#10; field &#61; optional&#40;string&#41;&#10; range &#61; optional&#40;object&#40;&#123;&#10; end &#61; number&#10; interval &#61; number&#10; start &#61; number&#10; &#125;&#41;&#41;&#10; time &#61; optional&#40;object&#40;&#123;&#10; type &#61; string&#10; expiration_ms &#61; optional&#40;number&#41;&#10; field &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#41;&#10; table_constraints &#61; optional&#40;object&#40;&#123;&#10; primary_key_columns &#61; optional&#40;list&#40;string&#41;&#41;&#10; foreign_keys &#61; optional&#40;object&#40;&#123;&#10; referenced_table &#61; object&#40;&#123;&#10; project_id &#61; string&#10; dataset_id &#61; string&#10; table_id &#61; string&#10; &#125;&#41;&#10; column_references &#61; object&#40;&#123;&#10; referencing_column &#61; string&#10; referenced_column &#61; string&#10; &#125;&#41;&#10; name &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [tag_bindings](variables.tf#L305) | Tag bindings for this dataset, in key => tag value id format. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> |
| [views](variables.tf#L312) | View definitions. | <code title="map&#40;object&#40;&#123;&#10; query &#61; string&#10; deletion_protection &#61; optional&#40;bool&#41;&#10; description &#61; optional&#40;string, &#34;Terraform managed.&#34;&#41;&#10; friendly_name &#61; optional&#40;string&#41;&#10; labels &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; use_legacy_sql &#61; optional&#40;bool&#41;&#10; schema &#61; optional&#40;list&#40;object&#40;&#123;&#10; name &#61; string&#10; type &#61; string&#10; description &#61; string&#10; mode &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [context](variables.tf#L68) | Context-specific interpolations. | <code title="object&#40;&#123;&#10; condition_vars &#61; optional&#40;map&#40;map&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; custom_roles &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; kms_keys &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; iam_principals &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; locations &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; project_ids &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; tag_values &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> |
| [dataset_access](variables.tf#L83) | Set access in the dataset resource instead of using separate resources. | <code>bool</code> | | <code>false</code> |
| [description](variables.tf#L89) | Optional description. | <code>string</code> | | <code>&#34;Terraform managed.&#34;</code> |
| [encryption_key](variables.tf#L95) | Self link of the KMS key that will be used to protect destination table. | <code>string</code> | | <code>null</code> |
| [friendly_name](variables.tf#L101) | Dataset friendly name. | <code>string</code> | | <code>null</code> |
| [iam](variables-iam.tf#L17) | IAM bindings in {ROLE => [MEMBERS]} format. Mutually exclusive with the access_* variables used for basic roles. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam_bindings](variables-iam.tf#L23) | Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary. | <code title="map&#40;object&#40;&#123;&#10; members &#61; list&#40;string&#41;&#10; role &#61; string&#10; condition &#61; optional&#40;object&#40;&#123;&#10; expression &#61; string&#10; title &#61; string&#10; description &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam_bindings_additive](variables-iam.tf#L38) | Individual additive IAM bindings. Keys are arbitrary. | <code title="map&#40;object&#40;&#123;&#10; member &#61; string&#10; role &#61; string&#10; condition &#61; optional&#40;object&#40;&#123;&#10; expression &#61; string&#10; title &#61; string&#10; description &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam_by_principals](variables-iam.tf#L53) | Authoritative IAM binding in {PRINCIPAL => [ROLES]} format. Principals need to be statically defined to avoid errors. Merged internally with the `iam` variable. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [labels](variables.tf#L116) | Dataset labels. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> |
| [location](variables.tf#L122) | Dataset location. | <code>string</code> | | <code>&#34;EU&#34;</code> |
| [materialized_views](variables.tf#L128) | Materialized views definitions. | <code title="map&#40;object&#40;&#123;&#10; query &#61; string&#10; allow_non_incremental_definition &#61; optional&#40;bool&#41;&#10; deletion_protection &#61; optional&#40;bool&#41;&#10; description &#61; optional&#40;string, &#34;Terraform managed.&#34;&#41;&#10; enable_refresh &#61; optional&#40;bool&#41;&#10; friendly_name &#61; optional&#40;string&#41;&#10; labels &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; refresh_interval_ms &#61; optional&#40;bool&#41;&#10; require_partition_filter &#61; optional&#40;bool&#41;&#10; options &#61; optional&#40;object&#40;&#123;&#10; clustering &#61; optional&#40;list&#40;string&#41;&#41;&#10; expiration_time &#61; optional&#40;number&#41;&#10; &#125;&#41;, &#123;&#125;&#41;&#10; partitioning &#61; optional&#40;object&#40;&#123;&#10; field &#61; optional&#40;string&#41;&#10; range &#61; optional&#40;object&#40;&#123;&#10; end &#61; number&#10; interval &#61; number&#10; start &#61; number&#10; &#125;&#41;&#41;&#10; time &#61; optional&#40;object&#40;&#123;&#10; type &#61; string&#10; expiration_ms &#61; optional&#40;number&#41;&#10; field &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [options](variables.tf#L161) | Dataset options. | <code title="object&#40;&#123;&#10; default_collation &#61; optional&#40;string&#41;&#10; default_table_expiration_ms &#61; optional&#40;number&#41;&#10; default_partition_expiration_ms &#61; optional&#40;number&#41;&#10; delete_contents_on_destroy &#61; optional&#40;bool, false&#41;&#10; is_case_insensitive &#61; optional&#40;bool&#41;&#10; max_time_travel_hours &#61; optional&#40;number, 168&#41;&#10; storage_billing_model &#61; optional&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> |
| [routines](variables.tf#L180) | Routine definitions. | <code title="map&#40;object&#40;&#123;&#10; description &#61; optional&#40;string&#41;&#10; routine_type &#61; string&#10; language &#61; optional&#40;string&#41;&#10; definition_body &#61; string&#10; imported_libraries &#61; optional&#40;list&#40;string&#41;&#41;&#10; determinism_level &#61; optional&#40;string&#41;&#10; data_governance_type &#61; optional&#40;string&#41;&#10; return_type &#61; optional&#40;string&#41;&#10; return_table_type &#61; optional&#40;string&#41;&#10; arguments &#61; optional&#40;map&#40;object&#40;&#123;&#10; argument_kind &#61; optional&#40;string&#41;&#10; mode &#61; optional&#40;string&#41;&#10; data_type &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;, &#123;&#125;&#41;&#10; spark_options &#61; optional&#40;object&#40;&#123;&#10; archive_uris &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; connection &#61; string&#10; container_image &#61; optional&#40;string&#41;&#10; file_uris &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; jar_uris &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; main_file_uri &#61; optional&#40;string&#41;&#10; main_class &#61; optional&#40;string&#41;&#10; properties &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; py_file_uris &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; runtime_version &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#10; remote_function_options &#61; optional&#40;object&#40;&#123;&#10; connection &#61; string&#10; endpoint &#61; optional&#40;string&#41;&#10; max_batching_rows &#61; optional&#40;string&#41;&#10; user_defined_context &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; &#125;&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [tables](variables.tf#L219) | Table definitions. Options and partitioning default to null. Partitioning can only use `range` or `time`, set the unused one to null. | <code title="map&#40;object&#40;&#123;&#10; deletion_protection &#61; optional&#40;bool&#41;&#10; description &#61; optional&#40;string, &#34;Terraform managed.&#34;&#41;&#10; friendly_name &#61; optional&#40;string&#41;&#10; labels &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; require_partition_filter &#61; optional&#40;bool&#41;&#10; schema &#61; optional&#40;string&#41;&#10; external_data_configuration &#61; optional&#40;object&#40;&#123;&#10; autodetect &#61; bool&#10; source_uris &#61; list&#40;string&#41;&#10; avro_logical_types &#61; optional&#40;bool&#41;&#10; compression &#61; optional&#40;string&#41;&#10; connection_id &#61; optional&#40;string&#41;&#10; file_set_spec_type &#61; optional&#40;string&#41;&#10; ignore_unknown_values &#61; optional&#40;bool&#41;&#10; metadata_cache_mode &#61; optional&#40;string&#41;&#10; object_metadata &#61; optional&#40;string&#41;&#10; json_options_encoding &#61; optional&#40;string&#41;&#10; reference_file_schema_uri &#61; optional&#40;string&#41;&#10; schema &#61; optional&#40;string&#41;&#10; source_format &#61; optional&#40;string&#41;&#10; max_bad_records &#61; optional&#40;number&#41;&#10; csv_options &#61; optional&#40;object&#40;&#123;&#10; quote &#61; string&#10; allow_jagged_rows &#61; optional&#40;bool&#41;&#10; allow_quoted_newlines &#61; optional&#40;bool&#41;&#10; encoding &#61; optional&#40;string&#41;&#10; field_delimiter &#61; optional&#40;string&#41;&#10; skip_leading_rows &#61; optional&#40;number&#41;&#10; &#125;&#41;&#41;&#10; google_sheets_options &#61; optional&#40;object&#40;&#123;&#10; range &#61; optional&#40;string&#41;&#10; skip_leading_rows &#61; optional&#40;number&#41;&#10; &#125;&#41;&#41;&#10; hive_partitioning_options &#61; optional&#40;object&#40;&#123;&#10; mode &#61; optional&#40;string&#41;&#10; require_partition_filter &#61; optional&#40;bool&#41;&#10; source_uri_prefix &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#10; parquet_options &#61; optional&#40;object&#40;&#123;&#10; enum_as_string &#61; optional&#40;bool&#41;&#10; enable_list_inference &#61; optional&#40;bool&#41;&#10; &#125;&#41;&#41;&#10;&#10;&#10; &#125;&#41;&#41;&#10; options &#61; optional&#40;object&#40;&#123;&#10; clustering &#61; optional&#40;list&#40;string&#41;&#41;&#10; encryption_key &#61; optional&#40;string&#41;&#10; expiration_time &#61; optional&#40;number&#41;&#10; max_staleness &#61; optional&#40;string&#41;&#10; &#125;&#41;, &#123;&#125;&#41;&#10; partitioning &#61; optional&#40;object&#40;&#123;&#10; field &#61; optional&#40;string&#41;&#10; range &#61; optional&#40;object&#40;&#123;&#10; end &#61; number&#10; interval &#61; number&#10; start &#61; number&#10; &#125;&#41;&#41;&#10; time &#61; optional&#40;object&#40;&#123;&#10; type &#61; string&#10; expiration_ms &#61; optional&#40;number&#41;&#10; field &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#41;&#10; table_constraints &#61; optional&#40;object&#40;&#123;&#10; primary_key_columns &#61; optional&#40;list&#40;string&#41;&#41;&#10; foreign_keys &#61; optional&#40;object&#40;&#123;&#10; referenced_table &#61; object&#40;&#123;&#10; project_id &#61; string&#10; dataset_id &#61; string&#10; table_id &#61; string&#10; &#125;&#41;&#10; column_references &#61; object&#40;&#123;&#10; referencing_column &#61; string&#10; referenced_column &#61; string&#10; &#125;&#41;&#10; name &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [tag_bindings](variables.tf#L304) | Tag bindings for this dataset, in key => tag value id format. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> |
| [views](variables.tf#L311) | View definitions. | <code title="map&#40;object&#40;&#123;&#10; query &#61; string&#10; deletion_protection &#61; optional&#40;bool&#41;&#10; description &#61; optional&#40;string, &#34;Terraform managed.&#34;&#41;&#10; friendly_name &#61; optional&#40;string&#41;&#10; labels &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; use_legacy_sql &#61; optional&#40;bool&#41;&#10; schema &#61; optional&#40;list&#40;object&#40;&#123;&#10; name &#61; string&#10; type &#61; string&#10; description &#61; string&#10; mode &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
## Outputs

View File

@@ -0,0 +1,82 @@
/**
* Copyright 2024 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.
*/
# tfdoc:file:description IAM bindings.
locals {
_iam_principal_roles = distinct(flatten(values(var.iam_by_principals)))
_iam_principals = {
for r in local._iam_principal_roles : r => [
for k, v in var.iam_by_principals :
k if try(index(v, r), null) != null
]
}
iam = {
for role in distinct(concat(keys(var.iam), keys(local._iam_principals))) :
role => concat(
try(var.iam[role], []),
try(local._iam_principals[role], [])
)
}
}
resource "google_bigquery_dataset_iam_binding" "authoritative" {
for_each = local.iam
project = local.project_id
dataset_id = google_bigquery_dataset.default.dataset_id
role = lookup(local.ctx.custom_roles, each.key, each.key)
members = [
for v in each.value : lookup(local.ctx.iam_principals, v, v)
]
}
resource "google_bigquery_dataset_iam_binding" "bindings" {
for_each = var.iam_bindings
project = local.project_id
dataset_id = google_bigquery_dataset.default.dataset_id
role = lookup(local.ctx.custom_roles, each.value.role, each.value.role)
members = [
for v in each.value.members : lookup(local.ctx.iam_principals, v, v)
]
dynamic "condition" {
for_each = each.value.condition == null ? [] : [""]
content {
expression = templatestring(
each.value.condition.expression, var.context.condition_vars
)
title = each.value.condition.title
description = each.value.condition.description
}
}
}
resource "google_bigquery_dataset_iam_member" "bindings" {
for_each = var.iam_bindings_additive
project = local.project_id
dataset_id = google_bigquery_dataset.default.dataset_id
role = lookup(local.ctx.custom_roles, each.value.role, each.value.role)
member = lookup(local.ctx.iam_principals, each.value.member, each.value.member)
dynamic "condition" {
for_each = each.value.condition == null ? [] : [""]
content {
expression = templatestring(
each.value.condition.expression, var.context.condition_vars
)
title = each.value.condition.title
description = each.value.condition.description
}
}
}

View File

@@ -231,15 +231,6 @@ resource "google_bigquery_dataset_access" "authorized_routines" {
}
}
resource "google_bigquery_dataset_iam_binding" "bindings" {
for_each = var.iam
project = local.project_id
dataset_id = google_bigquery_dataset.default.dataset_id
role = lookup(local.ctx.custom_roles, each.key, each.key)
members = [
for v in each.value : lookup(local.ctx.iam_principals, v, v)
]
}
resource "google_bigquery_table" "default" {
provider = google-beta

View File

@@ -0,0 +1,58 @@
/**
* Copyright 2024 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.
*/
variable "iam" {
description = "IAM bindings in {ROLE => [MEMBERS]} format. Mutually exclusive with the access_* variables used for basic roles."
type = map(list(string))
default = {}
}
variable "iam_bindings" {
description = "Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary."
type = map(object({
members = list(string)
role = string
condition = optional(object({
expression = string
title = string
description = optional(string)
}))
}))
nullable = false
default = {}
}
variable "iam_bindings_additive" {
description = "Individual additive IAM bindings. Keys are arbitrary."
type = map(object({
member = string
role = string
condition = optional(object({
expression = string
title = string
description = optional(string)
}))
}))
nullable = false
default = {}
}
variable "iam_by_principals" {
description = "Authoritative IAM binding in {PRINCIPAL => [ROLES]} format. Principals need to be statically defined to avoid errors. Merged internally with the `iam` variable."
type = map(list(string))
default = {}
nullable = false
}

View File

@@ -68,6 +68,7 @@ variable "authorized_views" {
variable "context" {
description = "Context-specific interpolations."
type = object({
condition_vars = optional(map(map(string)), {})
custom_roles = optional(map(string), {})
kms_keys = optional(map(string), {})
iam_principals = optional(map(string), {})
@@ -103,11 +104,9 @@ variable "friendly_name" {
default = null
}
variable "iam" {
description = "IAM bindings in {ROLE => [MEMBERS]} format. Mutually exclusive with the access_* variables used for basic roles."
type = map(list(string))
default = {}
}
variable "id" {
description = "Dataset id."

View File

@@ -442,7 +442,8 @@ module "project-factory" {
data_defaults = {
billing_account = var.billing_account_id
locations = {
storage = "EU"
bigquery = "EU"
storage = "EU"
}
}
# make sure the environment label and stackdriver service are always added
@@ -557,7 +558,6 @@ asset_feeds:
name: App 0
factories_config:
org_policies: data/factories/org-policies
pam_entitlements:
app-0-admins:
max_request_duration: 3600s
@@ -639,6 +639,8 @@ service_accounts:
iam_self_roles:
- roles/logging.logWriter
- roles/monitoring.metricWriter
tag_bindings:
context: $tag_values:context/project-factory
# this is just for illustrative/test purposes
iam:
roles/iam.serviceAccountUser:
@@ -672,6 +674,8 @@ billing_budgets:
buckets:
app-0-bucket-a:
location: europe-west8
tag_bindings:
context: $tag_values:context/gke
app-0-bucket-b:
location: europe-west8
logging_config:
@@ -695,6 +699,12 @@ services:
- container.googleapis.com
- pubsub.googleapis.com
- storage.googleapis.com
datasets:
test_0:
friendly_name: Test Dataset
iam:
roles/bigquery.dataViewer:
- $iam_principals:gcp-devops
pubsub_topics:
app-0-topic-a:
iam:
@@ -703,6 +713,14 @@ pubsub_topics:
app-0-topic-b:
subscriptions:
app-0-topic-b-sub: {}
kms:
keyrings:
my-keyring:
location: europe-west1
keys:
my-key: {}
tag_bindings:
context: $tag_values:context/project-factory
tags:
my-tag-key-1:
values:

View File

@@ -18,12 +18,26 @@ locals {
projects_bigquery_datasets = flatten([
for k, v in local.projects_input : [
for name, opts in lookup(v, "datasets", {}) : {
project_key = k
project_name = v.name
id = name
encryption_key = lookup(opts, "encryption_key", null)
friendly_name = lookup(opts, "friendly_name", null)
location = lookup(opts, "location", null)
project_key = k
project_name = v.name
id = name
encryption_key = lookup(opts, "encryption_key", null)
friendly_name = lookup(opts, "friendly_name", null)
location = lookup(opts, "location", null)
iam = lookup(opts, "iam", {})
iam_bindings = lookup(opts, "iam_bindings", {})
iam_bindings_additive = lookup(opts, "iam_bindings_additive", {})
iam_by_principals = lookup(opts, "iam_by_principals", {})
tag_bindings = lookup(opts, "tag_bindings", {})
options = {
default_collation = try(opts.options.default_collation, null)
default_table_expiration_ms = try(opts.options.default_table_expiration_ms, null)
default_partition_expiration_ms = try(opts.options.default_partition_expiration_ms, null)
delete_contents_on_destroy = try(opts.options.delete_contents_on_destroy, null)
is_case_insensitive = try(opts.options.is_case_insensitive, null)
max_time_travel_hours = try(opts.options.max_time_travel_hours, null)
storage_billing_model = try(opts.options.storage_billing_model, null)
}
}
]
])
@@ -46,6 +60,8 @@ module "bigquery-datasets" {
kms_keys = merge(local.ctx.kms_keys, local.kms_keys, local.kms_autokeys)
locations = local.ctx.locations
project_ids = local.ctx_project_ids
tag_keys = local.ctx_tag_keys
tag_values = local.ctx_tag_values
})
encryption_key = each.value.encryption_key
friendly_name = each.value.friendly_name
@@ -54,4 +70,10 @@ module "bigquery-datasets" {
lookup(each.value, "location", null),
local.data_defaults.defaults.locations.bigquery
)
iam = each.value.iam
iam_bindings = each.value.iam_bindings
iam_bindings_additive = each.value.iam_bindings_additive
iam_by_principals = each.value.iam_by_principals
tag_bindings = each.value.tag_bindings
options = each.value.options
}

View File

@@ -56,6 +56,7 @@ locals {
lifecycle_rules = lookup(opts, "lifecycle_rules", {})
logging_config = lookup(opts, "logging_config", null)
enable_object_retention = lookup(opts, "enable_object_retention", null)
tag_bindings = lookup(opts, "tag_bindings", {})
}
]
])
@@ -81,6 +82,8 @@ module "buckets" {
locations = local.ctx.locations
project_ids = local.ctx_project_ids
storage_buckets = local.ctx.storage_buckets
tag_keys = local.ctx_tag_keys
tag_values = local.ctx_tag_values
})
iam = each.value.iam
iam_bindings = each.value.iam_bindings
@@ -101,4 +104,5 @@ module "buckets" {
soft_delete_retention = each.value.soft_delete_retention
logging_config = each.value.logging_config
enable_object_retention = each.value.enable_object_retention
tag_bindings = each.value.tag_bindings
}

View File

@@ -25,6 +25,7 @@ locals {
iam = lookup(opts, "iam", {})
iam_bindings = lookup(opts, "iam_bindings", {})
iam_bindings_additive = lookup(opts, "iam_bindings_additive", {})
tag_bindings = lookup(opts, "tag_bindings", {})
keys = lookup(opts, "keys", {})
} if try(opts.location, null) != null
]
@@ -64,6 +65,7 @@ module "kms" {
iam = each.value.iam
iam_bindings = each.value.iam_bindings
iam_bindings_additive = each.value.iam_bindings_additive
tag_bindings = each.value.tag_bindings
keys = each.value.keys
context = merge(local.ctx, {
iam_principals = merge(
@@ -75,5 +77,7 @@ module "kms" {
)
locations = local.ctx.locations
project_ids = local.ctx_project_ids
tag_keys = local.ctx_tag_keys
tag_values = local.ctx_tag_values
})
}

View File

@@ -40,6 +40,7 @@ locals {
try(local.data_defaults.defaults.service_accounts.iam_self_roles, []),
))
iam_storage_roles = try(opts.iam_storage_roles, {})
tag_bindings = try(opts.tag_bindings, {})
opts = opts
}
]
@@ -85,6 +86,7 @@ module "service-accounts" {
display_name = each.value.display_name
context = merge(local.ctx, {
project_ids = local.ctx_project_ids
tag_values = local.ctx_tag_values
})
iam_project_roles = merge(
each.value.iam_project_roles,
@@ -92,6 +94,7 @@ module "service-accounts" {
"$project_ids:${each.value.project_key}" = each.value.iam_self_roles
}
)
tag_bindings = each.value.tag_bindings
}
module "service_accounts-iam" {

View File

@@ -244,6 +244,39 @@
},
"encryption_key": {
"type": "string"
},
"iam": {
"$ref": "#/$defs/iam"
},
"iam_bindings": {
"$ref": "#/$defs/iam_bindings"
},
"iam_bindings_additive": {
"$ref": "#/$defs/iam_bindings_additive"
},
"iam_by_principals": {
"$ref": "#/$defs/iam_by_principals"
},
"options": {
"type": "object",
"additionalProperties": false,
"properties": {
"default_table_expiration_ms": {
"type": "number"
},
"default_partition_expiration_ms": {
"type": "number"
},
"delete_contents_on_destroy": {
"type": "boolean"
},
"max_time_travel_hours": {
"type": "number"
}
}
},
"tag_bindings": {
"$ref": "#/$defs/tag_bindings"
}
}
}
@@ -348,6 +381,9 @@
"iam_bindings_additive": {
"$ref": "#/$defs/iam_bindings_additive"
},
"tag_bindings": {
"$ref": "#/$defs/tag_bindings"
},
"keys": {
"type": "object",
"additionalProperties": false,
@@ -651,6 +687,9 @@
},
"iam_sa_roles": {
"$ref": "#/$defs/iam_sa_roles"
},
"tag_bindings": {
"$ref": "#/$defs/tag_bindings"
}
}
}
@@ -756,15 +795,6 @@
}
}
},
"tag_bindings": {
"type": "object",
"additionalProperties": false,
"patternProperties": {
"^[a-z0-9_-]+$": {
"type": "string"
}
}
},
"tags": {
"type": "object",
"additionalProperties": {
@@ -813,6 +843,15 @@
}
}
},
"tag_bindings": {
"type": "object",
"additionalProperties": false,
"patternProperties": {
"^[a-z0-9_-]+$": {
"type": "string"
}
}
},
"universe": {
"type": "object",
"additionalProperties": false,
@@ -1192,6 +1231,9 @@
},
"enable_object_retention": {
"type": "boolean"
},
"tag_bindings": {
"$ref": "#/$defs/tag_bindings"
}
}
},
@@ -1804,6 +1846,15 @@
}
}
}
},
"tag_bindings": {
"type": "object",
"additionalProperties": false,
"patternProperties": {
"^[a-z0-9_-]+$": {
"type": "string"
}
}
}
}
}

View File

@@ -34,14 +34,14 @@ values:
terraform_labels:
goog-terraform-provisioned: 'true'
timeouts: null
google_bigquery_dataset_iam_binding.bindings["$custom_roles:myrole_one"]:
google_bigquery_dataset_iam_binding.authoritative["$custom_roles:myrole_one"]:
condition: []
dataset_id: dataset_0
members:
- user:test-user@example.com
project: foo-test-0
role: organizations/366118655033/roles/myRoleOne
google_bigquery_dataset_iam_binding.bindings["roles/viewer"]:
google_bigquery_dataset_iam_binding.authoritative["roles/viewer"]:
condition: []
dataset_id: dataset_0
members:

View File

@@ -15,15 +15,39 @@
values:
module.bigquery-dataset.google_bigquery_dataset.default:
dataset_id: my_dataset
default_encryption_configuration: []
default_partition_expiration_ms: null
default_table_expiration_ms: null
delete_contents_on_destroy: false
description: Terraform managed.
effective_labels:
goog-terraform-provisioned: 'true'
external_catalog_dataset_options: []
external_dataset_reference: []
friendly_name: null
labels: null
location: EU
max_time_travel_hours: '168'
project: my-project
module.bigquery-dataset.google_bigquery_dataset_iam_binding.bindings["roles/bigquery.dataOwner"]:
resource_tags: null
terraform_labels:
goog-terraform-provisioned: 'true'
timeouts: null
module.bigquery-dataset.google_bigquery_dataset_iam_binding.authoritative["roles/bigquery.dataOwner"]:
condition: []
dataset_id: my_dataset
members:
- user:user1@example.org
project: my-project
role: roles/bigquery.dataOwner
module.bigquery-dataset.google_bigquery_dataset_iam_binding.bindings["reader_user"]:
condition: []
dataset_id: my_dataset
members:
- user:user2@example.org
project: my-project
role: roles/bigquery.dataViewer
counts:
google_bigquery_dataset: 1
google_bigquery_dataset_iam_binding: 1
google_bigquery_dataset_iam_binding: 2

View File

@@ -81,6 +81,33 @@ values:
member: serviceAccount:dev-tb-app0-0-rw@test-pf-teams-iac-0.iam.gserviceaccount.com
project: test-pf-teams-iac-0
timeouts: null
module.project-factory.module.bigquery-datasets["dev-ta-app0-be/test_0"].google_bigquery_dataset.default:
dataset_id: test_0
default_encryption_configuration: []
default_partition_expiration_ms: null
default_table_expiration_ms: null
delete_contents_on_destroy: false
description: Terraform managed.
effective_labels:
goog-terraform-provisioned: 'true'
external_catalog_dataset_options: []
external_dataset_reference: []
friendly_name: Test Dataset
labels: null
location: EU
max_time_travel_hours: '168'
project: test-pf-dev-ta-app0-be
resource_tags: null
terraform_labels:
goog-terraform-provisioned: 'true'
timeouts: null
? module.project-factory.module.bigquery-datasets["dev-ta-app0-be/test_0"].google_bigquery_dataset_iam_binding.authoritative["roles/bigquery.dataViewer"]
: condition: []
dataset_id: test_0
members:
- group:gcp-devops@example.org
project: test-pf-dev-ta-app0-be
role: roles/bigquery.dataViewer
module.project-factory.module.billing-budgets[0].google_billing_budget.default["test-100"]:
all_updates_rule:
- disable_default_iam_recipients: true
@@ -148,6 +175,11 @@ values:
uniform_bucket_level_access: true
versioning:
- enabled: false
module.project-factory.module.buckets["dev-ta-app0-be/app-0-bucket-a"].google_tags_location_tag_binding.binding["context"]:
location: europe-west8
parent: //storage.googleapis.com/projects/_/buckets/test-pf-dev-ta-app0-be-app-0-bucket-a
tag_value: tagValues/654321
timeouts: null
module.project-factory.module.buckets["dev-ta-app0-be/app-0-bucket-b"].google_storage_bucket.bucket[0]:
autoclass: []
cors: []
@@ -288,6 +320,26 @@ values:
display_name: App X
tags: null
timeouts: null
module.project-factory.module.kms["dev-ta-app0-be/my-keyring"].google_kms_crypto_key.default["my-key"]:
effective_labels:
goog-terraform-provisioned: 'true'
labels: null
name: my-key
purpose: ENCRYPT_DECRYPT
rotation_period: null
skip_initial_version_creation: false
terraform_labels:
goog-terraform-provisioned: 'true'
timeouts: null
module.project-factory.module.kms["dev-ta-app0-be/my-keyring"].google_kms_key_ring.default[0]:
location: europe-west1
name: my-keyring
project: test-pf-dev-ta-app0-be
timeouts: null
module.project-factory.module.kms["dev-ta-app0-be/my-keyring"].google_tags_location_tag_binding.binding["context"]:
location: europe-west1
tag_value: $tag_values:context/project-factory
timeouts: null
? module.project-factory.module.projects-iam["dev-ta-app0-be"].google_compute_shared_vpc_service_project.shared_vpc_service[0]
: deletion_policy: null
host_project: $project_ids:dev-spoke-0
@@ -819,6 +871,9 @@ values:
member: serviceAccount:app-0-be@test-pf-dev-ta-app0-be.iam.gserviceaccount.com
project: test-pf-dev-ta-app0-be
timeouts: null
module.project-factory.module.service-accounts["dev-ta-app0-be/app-0-be"].google_tags_tag_binding.binding["context"]:
tag_value: $tag_values:context/project-factory
timeouts: null
? module.project-factory.module.service-accounts["dev-ta-app0-be/app-0-fe"].google_project_iam_member.project-roles["$project_ids:dev-spoke-0-roles/compute.networkUser"]
: condition: []
project: $project_ids:dev-spoke-0
@@ -905,6 +960,8 @@ values:
triggers_replace: null
counts:
google_bigquery_dataset: 1
google_bigquery_dataset_iam_binding: 1
google_billing_budget: 1
google_cloud_asset_folder_feed: 1
google_compute_shared_vpc_host_project: 1
@@ -915,7 +972,9 @@ counts:
google_folder_iam_binding: 1
google_iam_workload_identity_pool: 1
google_iam_workload_identity_pool_provider: 1
google_kms_crypto_key: 1
google_kms_crypto_key_iam_member: 2
google_kms_key_ring: 1
google_monitoring_notification_channel: 1
google_org_policy_policy: 3
google_privileged_access_manager_entitlement: 2
@@ -934,10 +993,11 @@ counts:
google_storage_bucket: 3
google_storage_bucket_iam_binding: 2
google_storage_project_service_account: 4
google_tags_tag_binding: 2
google_tags_location_tag_binding: 2
google_tags_tag_binding: 3
google_tags_tag_key: 1
google_tags_tag_value: 2
google_tags_tag_value_iam_binding: 1
modules: 32
resources: 111
modules: 34
resources: 118
terraform_data: 2