diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 862072ceb..74ac583d2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -442,7 +442,7 @@ For example, resource management stages only export three map variables: `folder ```hcl variable "folder_ids" { - # tfdoc:variable:source 01-resman + # tfdoc:variable:source 0-org-setup description = "Folders to be used for the networking resources in folders/nnnnnnnnnnn format. If null, folder will be created." type = object({ networking = string diff --git a/fast/addons/2-networking-ngfw/README.md b/fast/addons/2-networking-ngfw/README.md index bc534c513..8de37c378 100644 --- a/fast/addons/2-networking-ngfw/README.md +++ b/fast/addons/2-networking-ngfw/README.md @@ -138,7 +138,7 @@ The preconfigured provider file uses impersonation to run with this stage's auto Variables in this stage -- like most other FAST stages -- are broadly divided into three separate sets: - variables which refer to global values for the whole organization (org id, billing account id, prefix, etc.), which are pre-populated via the `0-globals.auto.tfvars.json` file linked or copied above -- variables which refer to resources managed by previous stages, which are prepopulated here via the `0-org-setup.auto.tfvars.json`, `1-resman.auto.tfvars.json` and `2-networking.auto.tfvars.json` files linked or copied above +- variables which refer to resources managed by previous stages, which are prepopulated here via the `0-org-setup.auto.tfvars.json`, `2-networking.auto.tfvars.json` files linked or copied above - and finally variables that optionally control this stage's behaviour and customizations, and can to be set in a custom `terraform.tfvars` file The first two sets are defined in the `variables-fast.tf` file, the latter set in the `variables.tf` file. The full list of variables can be found in the [Variables](#variables) table at the bottom of this document. diff --git a/fast/addons/2-networking-swp/README.md b/fast/addons/2-networking-swp/README.md index 355f7d279..3070a4d28 100644 --- a/fast/addons/2-networking-swp/README.md +++ b/fast/addons/2-networking-swp/README.md @@ -64,7 +64,6 @@ ln -s ~/fast-config/providers/2-networking-swp-providers.tf ./ # input files from other stages ln -s ~/fast-config/tfvars/0-globals.auto.tfvars.json ./ ln -s ~/fast-config/tfvars/0-org-setup.auto.tfvars.json ./ -ln -s ~/fast-config/tfvars/1-resman.auto.tfvars.json ./ ln -s ~/fast-config/tfvars/2-networking.auto.tfvars.json ./ # conventional place for stage tfvars (manually created) @@ -83,7 +82,7 @@ The preconfigured provider file uses impersonation to run with this stage's auto Variables in this stage -- like most other FAST stages -- are broadly divided into three separate sets: - variables which refer to global values for the whole organization (org id, billing account id, prefix, etc.), which are pre-populated via the `0-globals.auto.tfvars.json` file linked or copied above -- variables which refer to resources managed by previous stages, which are prepopulated here via the `0-org-setup.auto.tfvars.json`, `1-resman.auto.tfvars.json` and `2-networking.auto.tfvars.json` files linked or copied above +- variables which refer to resources managed by previous stages, which are prepopulated here via the `0-org-setup.auto.tfvars.json`, `2-networking.auto.tfvars.json` files linked or copied above - and finally variables that optionally control this stage's behaviour and customizations, and can to be set in a custom `terraform.tfvars` file The first two sets are defined in the `variables-fast.tf` file, the latter set in the `variables.tf` file. The full list of variables can be found in the [Variables](#variables) table at the bottom of this document. diff --git a/fast/addons/2-networking-test/README.md b/fast/addons/2-networking-test/README.md index 6217b07ae..b03b21887 100644 --- a/fast/addons/2-networking-test/README.md +++ b/fast/addons/2-networking-test/README.md @@ -64,7 +64,7 @@ The preconfigured provider file uses impersonation to run with this stage's auto Variables in this stage -- like most other FAST stages -- are broadly divided into three separate sets: - variables which refer to global values for the whole organization (org id, billing account id, prefix, etc.), which are pre-populated via the `0-globals.auto.tfvars.json` file linked or copied above -- variables which refer to resources managed by previous stages, which are prepopulated here via the `0-org-setup.auto.tfvars.json`, `1-resman.auto.tfvars.json` and `2-networking.auto.tfvars.json` files linked or copied above +- variables which refer to resources managed by previous stages, which are prepopulated here via the `0-org-setup.auto.tfvars.json`, `2-networking.auto.tfvars.json` files linked or copied above - and finally variables that optionally control this stage's behaviour and customizations, and can to be set in a custom `terraform.tfvars` file The first two sets are defined in the `variables-fast.tf` file, the latter set in the `variables.tf` file. The full list of variables can be found in the [Variables](#variables) table at the bottom of this document. diff --git a/fast/extras/0-cicd-github/README.md b/fast/extras/0-cicd-github/README.md index 3627a1b7d..3e1acd477 100644 --- a/fast/extras/0-cicd-github/README.md +++ b/fast/extras/0-cicd-github/README.md @@ -87,27 +87,15 @@ This is an example that creates repositories for stages 00 and 01, and populates ```tfvars repositories = { - fast_00_bootstrap = { + fast_00_org_setup = { create_options = { - description = "FAST bootstrap." + description = "FAST org setup." features = { issues = true } } populate_from = "../../stages/0-org-setup" } - fast_01_resman = { - create_options = { - description = "FAST resource management." - features = { - issues = true - } - } - populate_from = "../../stages/1-resman" - } - fast_02_networking = { - populate_from = "../../stages/2-networking-peering" - } } # tftest skip ``` @@ -120,9 +108,9 @@ Each repository may contain some sample tfvars and data files that can be used a ```tfvars repositories = { - fast_00_bootstrap = { + fast_00_org_setup = { create_options = { - description = "FAST bootstrap." + description = "FAST org setup." features = { issues = true } @@ -130,20 +118,6 @@ repositories = { populate_from = "../../stages/0-org-setup" populate_samples = true } - fast_01_resman = { - create_options = { - description = "FAST resource management." - features = { - issues = true - } - } - populate_from = "../../stages/1-resman" - populate_samples = true - } - fast_02_networking = { - populate_from = "../../stages/2-networking-peering" - populate_samples = true - } } # tftest skip diff --git a/fast/extras/0-cicd-gitlab/README.md b/fast/extras/0-cicd-gitlab/README.md index cae871fe2..9df02e2d2 100644 --- a/fast/extras/0-cicd-gitlab/README.md +++ b/fast/extras/0-cicd-gitlab/README.md @@ -149,38 +149,16 @@ This is an example that creates repositories for stages 00 and 01 and 02: ```tfvars projects = { - fast_00_bootstrap = { + fast_0_org_setup = { create_options = { - description = "FAST bootstrap." + description = "FAST org setup." features = { issues = true } } group = "org-admins" populate_from = "../../stages/0-org-setup" - workflow_file = "bootstrap-workflow.yaml" - } - fast_01_resman = { - create_options = { - description = "FAST resource management." - features = { - issues = true - } - } - group = "org-admins" - populate_from = "../../stages/1-resman" - workflow_file = "resman-workflow.yaml" - } - fast_02_networking = { - create_options = { - description = "FAST networking management." - features = { - issues = true - } - } - group = "net-admins" - populate_from = "../../stages/2-networking-legacy-a-peering" - workflow_file = "networking-workflow.yaml" + workflow_file = "org-setup-workflow.yaml" } } # tftest skip @@ -203,9 +181,9 @@ attribute to `true`. Here's an updated example: ```tfvars projects = { - fast_00_bootstrap = { + fast_0_org_setup = { create_options = { - description = "FAST bootstrap." + description = "FAST org setup." features = { issues = true } @@ -213,31 +191,7 @@ projects = { group = "org-admins" populate_from = "../../stages/0-org-setup" populate_sample = true - workflow_file = "bootstrap-workflow.yaml" - } - fast_01_resman = { - create_options = { - description = "FAST resource management." - features = { - issues = true - } - } - group = "org-admins" - populate_from = "../../stages/1-resman" - populate_sample = true - workflow_file = "resman-workflow.yaml" - } - fast_02_networking = { - create_options = { - description = "FAST networking management." - features = { - issues = true - } - } - group = "net-admins" - populate_from = "../../stages/2-networking-legacy-a-peering" - populate_sample = true - workflow_file = "networking-workflow.yaml" + workflow_file = "org-setup-workflow.yaml" } } # tftest skip @@ -258,9 +212,9 @@ initialization. ```tfvars projects = { - fast_00_bootstrap = { + fast_0_org_setup = { create_options = { - description = "FAST bootstrap." + description = "FAST org setup." features = { issues = true } @@ -268,31 +222,7 @@ projects = { group = "org-admins" populate_from = "../../stages/0-org-setup" populate_sample = true - workflow_file = "bootstrap-workflow.yaml" - } - fast_01_resman = { - create_options = { - description = "FAST resource management." - features = { - issues = true - } - } - group = "org-admins" - populate_from = "../../stages/1-resman" - populate_sample = true - workflow_file = "resman-workflow.yaml" - } - fast_02_networking = { - create_options = { - description = "FAST networking management." - features = { - issues = true - } - } - group = "net-admins" - populate_from = "../../stages/2-networking-legacy-a-peering" - populate_sample = true - workflow_file = "networking-workflow.yaml" + workflow_file = "org-setup-workflow.yaml" } } @@ -302,16 +232,6 @@ groups = { path = "gcp-org-admins" description = "GCP Organization administrators" } - net-admins = { - name = "gcp-net-admins" - path = "gcp-net-admins" - description = "GCP Network administrators" - } - shared = { - name = "shared" - path = "shared" - description = "Shared repositories" - } } # tftest skip ``` @@ -328,16 +248,6 @@ groups = { path = "gcp-org-admins" description = "GCP Organization administrators" } - net-admins = { - name = "gcp-net-admins" - path = "gcp-net-admins" - description = "GCP Network administrators" - } - shared = { - name = "shared" - path = "shared" - description = "Shared repositories" - } } # tftest skip ``` diff --git a/fast/extras/0-cicd-gitlab/terraform.tfvars.sample b/fast/extras/0-cicd-gitlab/terraform.tfvars.sample index 6f114beb9..367aa75ea 100644 --- a/fast/extras/0-cicd-gitlab/terraform.tfvars.sample +++ b/fast/extras/0-cicd-gitlab/terraform.tfvars.sample @@ -14,41 +14,17 @@ modules_config = { group = "shared" } projects = { - fast_00_bootstrap = { + fast_0_org_setup = { create_options = { - description = "FAST bootstrap." + description = "FAST org setup." features = { issues = true } } group = "org-admins" - populate_from = "../../stages/0-bootstrap" + populate_from = "../../stages/0-org-setup" populate_samples = true - workflow_file = "bootstrap-workflow.yaml" - } - fast_01_resman = { - create_options = { - description = "FAST resource management." - features = { - issues = true - } - } - group = "org-admins" - populate_from = "../../stages/1-resman" - populate_samples = true - workflow_file = "resman-workflow.yaml" - } - fast_02_networking = { - create_options = { - description = "FAST networking management." - features = { - issues = true - } - } - group = "net-admins" - populate_from = "../../stages/2-networking-legacy-a-peering" - populate_samples = true - workflow_file = "networking-workflow.yaml" + workflow_file = "org-setup-workflow.yaml" } } groups = { @@ -57,14 +33,4 @@ groups = { path = "gcp-org-admins" description = "GCP Organization administrators" } - net-admins = { - name = "gcp-net-admins" - path = "gcp-net-admins" - description = "GCP Network administrators" - } - shared = { - name = "shared" - path = "shared" - description = "Shared repositories" - } } \ No newline at end of file diff --git a/fast/stages/0-org-setup/schemas/project.schema.json b/fast/stages/0-org-setup/schemas/project.schema.json index 87866ff86..4881ece7c 100644 --- a/fast/stages/0-org-setup/schemas/project.schema.json +++ b/fast/stages/0-org-setup/schemas/project.schema.json @@ -1121,6 +1121,21 @@ } } }, + "logging_config": { + "type": "object", + "additionalProperties": false, + "properties": { + "log_bucket": { + "type": "string" + }, + "log_object_prefix": { + "type": "string" + } + }, + "required": [ + "log_bucket" + ] + }, "location": { "type": "string" }, diff --git a/fast/stages/0-org-setup/schemas/project.schema.md b/fast/stages/0-org-setup/schemas/project.schema.md index f4b2057e6..5e7f57c9b 100644 --- a/fast/stages/0-org-setup/schemas/project.schema.md +++ b/fast/stages/0-org-setup/schemas/project.schema.md @@ -308,6 +308,10 @@ - **num_newer_versions**: *number* - **with_state**: *string*
*enum: ['LIVE', 'ARCHIVED', 'ANY']* + - **logging_config**: *object* +
*additional properties: false* + - **log_bucket**: *string* + - **log_object_prefix**: *string* - **location**: *string* - **managed_folders**: *object*
*additional properties: false* diff --git a/fast/stages/1-vpcsc/IAM.md b/fast/stages/1-vpcsc/IAM.md deleted file mode 100644 index e6c31d31e..000000000 --- a/fast/stages/1-vpcsc/IAM.md +++ /dev/null @@ -1,18 +0,0 @@ -# IAM bindings reference - -Legend: + additive, conditional. - -## Project dev-sec-core-0 - -| members | roles | -|---|---| -|dev-resman-dp-0
serviceAccount|[roles/cloudkms.viewer](https://cloud.google.com/iam/docs/understanding-roles#cloudkms.viewer)
[roles/cloudkms.admin](https://cloud.google.com/iam/docs/understanding-roles#cloudkms.admin) +| -|dev-resman-pf-0
serviceAccount|[roles/cloudkms.viewer](https://cloud.google.com/iam/docs/understanding-roles#cloudkms.viewer)
[roles/cloudkms.admin](https://cloud.google.com/iam/docs/understanding-roles#cloudkms.admin) +| -|prod-resman-pf-0
serviceAccount|[roles/cloudkms.viewer](https://cloud.google.com/iam/docs/understanding-roles#cloudkms.viewer)
[roles/cloudkms.admin](https://cloud.google.com/iam/docs/understanding-roles#cloudkms.admin) +| - -## Project prod-sec-core-0 - -| members | roles | -|---|---| -|prod-resman-dp-0
serviceAccount|[roles/cloudkms.viewer](https://cloud.google.com/iam/docs/understanding-roles#cloudkms.viewer)
[roles/cloudkms.admin](https://cloud.google.com/iam/docs/understanding-roles#cloudkms.admin) +| -|prod-resman-pf-0
serviceAccount|[roles/cloudkms.viewer](https://cloud.google.com/iam/docs/understanding-roles#cloudkms.viewer)
[roles/cloudkms.admin](https://cloud.google.com/iam/docs/understanding-roles#cloudkms.admin) +| diff --git a/fast/stages/2-networking/schemas/project.schema.json b/fast/stages/2-networking/schemas/project.schema.json index 87866ff86..4881ece7c 100644 --- a/fast/stages/2-networking/schemas/project.schema.json +++ b/fast/stages/2-networking/schemas/project.schema.json @@ -1121,6 +1121,21 @@ } } }, + "logging_config": { + "type": "object", + "additionalProperties": false, + "properties": { + "log_bucket": { + "type": "string" + }, + "log_object_prefix": { + "type": "string" + } + }, + "required": [ + "log_bucket" + ] + }, "location": { "type": "string" }, diff --git a/fast/stages/2-networking/schemas/project.schema.md b/fast/stages/2-networking/schemas/project.schema.md index f4b2057e6..5e7f57c9b 100644 --- a/fast/stages/2-networking/schemas/project.schema.md +++ b/fast/stages/2-networking/schemas/project.schema.md @@ -308,6 +308,10 @@ - **num_newer_versions**: *number* - **with_state**: *string*
*enum: ['LIVE', 'ARCHIVED', 'ANY']* + - **logging_config**: *object* +
*additional properties: false* + - **log_bucket**: *string* + - **log_object_prefix**: *string* - **location**: *string* - **managed_folders**: *object*
*additional properties: false* diff --git a/fast/stages/2-project-factory/schemas/project.schema.json b/fast/stages/2-project-factory/schemas/project.schema.json index 87866ff86..4881ece7c 100644 --- a/fast/stages/2-project-factory/schemas/project.schema.json +++ b/fast/stages/2-project-factory/schemas/project.schema.json @@ -1121,6 +1121,21 @@ } } }, + "logging_config": { + "type": "object", + "additionalProperties": false, + "properties": { + "log_bucket": { + "type": "string" + }, + "log_object_prefix": { + "type": "string" + } + }, + "required": [ + "log_bucket" + ] + }, "location": { "type": "string" }, diff --git a/fast/stages/2-project-factory/schemas/project.schema.md b/fast/stages/2-project-factory/schemas/project.schema.md index f4b2057e6..5e7f57c9b 100644 --- a/fast/stages/2-project-factory/schemas/project.schema.md +++ b/fast/stages/2-project-factory/schemas/project.schema.md @@ -308,6 +308,10 @@ - **num_newer_versions**: *number* - **with_state**: *string*
*enum: ['LIVE', 'ARCHIVED', 'ANY']* + - **logging_config**: *object* +
*additional properties: false* + - **log_bucket**: *string* + - **log_object_prefix**: *string* - **location**: *string* - **managed_folders**: *object*
*additional properties: false* diff --git a/fast/stages/2-security/schemas/project.schema.json b/fast/stages/2-security/schemas/project.schema.json index 87866ff86..4881ece7c 100644 --- a/fast/stages/2-security/schemas/project.schema.json +++ b/fast/stages/2-security/schemas/project.schema.json @@ -1121,6 +1121,21 @@ } } }, + "logging_config": { + "type": "object", + "additionalProperties": false, + "properties": { + "log_bucket": { + "type": "string" + }, + "log_object_prefix": { + "type": "string" + } + }, + "required": [ + "log_bucket" + ] + }, "location": { "type": "string" }, diff --git a/fast/stages/2-security/schemas/project.schema.md b/fast/stages/2-security/schemas/project.schema.md index f4b2057e6..5e7f57c9b 100644 --- a/fast/stages/2-security/schemas/project.schema.md +++ b/fast/stages/2-security/schemas/project.schema.md @@ -308,6 +308,10 @@ - **num_newer_versions**: *number* - **with_state**: *string*
*enum: ['LIVE', 'ARCHIVED', 'ANY']* + - **logging_config**: *object* +
*additional properties: false* + - **log_bucket**: *string* + - **log_object_prefix**: *string* - **location**: *string* - **managed_folders**: *object*
*additional properties: false* diff --git a/fast/stages/3-data-platform-dev/README.md b/fast/stages/3-data-platform-dev/README.md index c42de2fa4..fef424ed1 100644 --- a/fast/stages/3-data-platform-dev/README.md +++ b/fast/stages/3-data-platform-dev/README.md @@ -201,7 +201,6 @@ ln -s ~/fast-config/providers/3-data-platform-dev-providers.tf ./ # input files from other stages ln -s ~/fast-config/tfvars/0-globals.auto.tfvars.json ./ ln -s ~/fast-config/tfvars/0-org-setup.auto.tfvars.json ./ -ln -s ~/fast-config/tfvars/1-resman.auto.tfvars.json ./ # conventional location for this stage terraform.tfvars (manually managed) ln -s ~/fast-config/3-data-platform-dev.auto.tfvars ./ @@ -222,7 +221,6 @@ gcloud storage cp gs://xxx-prod-iac-core-outputs-0/providers/3-data-platform-dev # input files from other stages gcloud storage cp gs://xxx-prod-iac-core-outputs-0/tfvars/0-globals.auto.tfvars.json ./ gcloud storage cp gs://xxx-prod-iac-core-outputs-0/tfvars/0-org-setup.auto.tfvars.json ./ -gcloud storage cp gs://xxx-prod-iac-core-outputs-0/tfvars/1-resman.auto.tfvars.json ./ # conventional location for this stage terraform.tfvars (manually managed) gcloud storage cp gs://xxx-prod-iac-core-outputs-0/3-data-platform-dev.auto.tfvars ./ @@ -347,14 +345,14 @@ The following table lists the available substitutions. |---|---|:---:|:---:|:---:|:---:| | [automation](variables-fast.tf#L17) | Automation resources created by the bootstrap stage. | object({…}) | ✓ | | 0-org-setup | | [billing_account](variables-fast.tf#L26) | Billing account id. If billing account is not part of the same org set `is_org_level` to false. | object({…}) | ✓ | | 0-org-setup | -| [environments](variables-fast.tf#L34) | Environment names. | object({…}) | ✓ | | 1-resman | +| [environments](variables-fast.tf#L34) | Environment names. | object({…}) | ✓ | | 0-org-setup | | [prefix](variables-fast.tf#L69) | Prefix used for resources that need unique names. Use a maximum of 9 chars for organizations, and 11 chars for tenants. | string | ✓ | | 0-org-setup | | [aspect_types](variables.tf#L17) | Aspect templates. Merged with those defined via the factory. | map(object({…})) | | {} | | | [central_project_config](variables.tf#L48) | Configuration for the top-level central project. | object({…}) | | {} | | | [encryption_keys](variables.tf#L90) | Default encryption keys for services, in service => { region => key id } format. Overridable on a per-object basis. | object({…}) | | {} | | | [exposure_config](variables.tf#L101) | Data exposure configuration. | object({…}) | | {} | | | [factories_config](variables.tf#L119) | Configuration for the resource factories. | object({…}) | | {} | | -| [folder_ids](variables-fast.tf#L45) | Folder name => id mappings. | map(string) | | {} | 1-resman | +| [folder_ids](variables-fast.tf#L45) | Folder name => id mappings. | map(string) | | {} | 0-org-setup | | [host_project_ids](variables-fast.tf#L53) | Shared VPC host project name => id mappings. | map(string) | | {} | 2-networking | | [kms_keys](variables-fast.tf#L61) | KMS key ids. | map(string) | | {} | 2-security | | [location](variables.tf#L134) | Default location used when no location is specified. | string | | "europe-west1" | | @@ -363,7 +361,7 @@ The following table lists the available substitutions. | [secure_tags](variables.tf#L147) | Resource manager tags created in the central project. | map(object({…})) | | {} | | | [stage_config](variables.tf#L168) | Stage configuration used to find environment and resource ids, and to generate names. | object({…}) | | {…} | | | [subnet_self_links](variables-fast.tf#L87) | Subnet VPC name => { name => self link } mappings. | map(map(string)) | | {} | 2-networking | -| [tag_values](variables-fast.tf#L95) | FAST-managed resource manager tag values. | map(string) | | {} | 1-resman | +| [tag_values](variables-fast.tf#L95) | FAST-managed resource manager tag values. | map(string) | | {} | 0-org-setup | | [vpc_self_links](variables-fast.tf#L103) | Shared VPC name => self link mappings. | map(string) | | {} | 2-networking | ## Outputs diff --git a/fast/stages/3-data-platform-dev/variables-fast.tf b/fast/stages/3-data-platform-dev/variables-fast.tf index 0f42f325c..82b096909 100644 --- a/fast/stages/3-data-platform-dev/variables-fast.tf +++ b/fast/stages/3-data-platform-dev/variables-fast.tf @@ -32,7 +32,7 @@ variable "billing_account" { } variable "environments" { - # tfdoc:variable:source 1-resman + # tfdoc:variable:source 0-org-setup description = "Environment names." type = object({ dev = object({ @@ -43,7 +43,7 @@ variable "environments" { } variable "folder_ids" { - # tfdoc:variable:source 1-resman + # tfdoc:variable:source 0-org-setup description = "Folder name => id mappings." type = map(string) nullable = false @@ -93,7 +93,7 @@ variable "subnet_self_links" { } variable "tag_values" { - # tfdoc:variable:source 1-resman + # tfdoc:variable:source 0-org-setup description = "FAST-managed resource manager tag values." type = map(string) nullable = false diff --git a/fast/stages/3-gcve-dev/README.md b/fast/stages/3-gcve-dev/README.md index 7d382003e..8f2aebf57 100644 --- a/fast/stages/3-gcve-dev/README.md +++ b/fast/stages/3-gcve-dev/README.md @@ -110,7 +110,6 @@ ln -s ~/fast-config/fast-test-00/providers/3-gcve-dev-providers.tf ./ # input files from other stages ln -s ~/fast-config/fast-test-00/tfvars/0-globals.auto.tfvars.json ./ ln -s ~/fast-config/fast-test-00/tfvars/0-org-setup.auto.tfvars.json ./ -ln -s ~/fast-config/fast-test-00/tfvars/1-resman.auto.tfvars.json ./ ln -s ~/fast-config/fast-test-00/tfvars/2-networking.auto.tfvars.json ./ # conventional place for stage tfvars (manually created) @@ -128,7 +127,6 @@ gcloud storage cp gs://xxx-prod-iac-core-outputs-0/providers/3-gcve-dev-provider # input files from other stages gcloud storage cp gs://xxx-prod-iac-core-outputs-0/tfvars/0-globals.auto.tfvars.json ./ gcloud storage cp gs://xxx-prod-iac-core-outputs-0/tfvars/0-org-setup.auto.tfvars.json ./ -gcloud storage cp gs://xxx-prod-iac-core-outputs-0/tfvars/1-resman.auto.tfvars.json ./ gcloud storage cp gs://xxx-prod-iac-core-outputs-0/tfvars/2-networking.auto.tfvars.json ./ # conventional place for stage tfvars (manually created) @@ -176,9 +174,9 @@ terraform apply |---|---|:---:|:---:|:---:|:---:| | [automation](variables-fast.tf#L19) | Automation resources created by the bootstrap stage. | object({…}) | ✓ | | 0-org-setup | | [billing_account](variables-fast.tf#L28) | Billing account id. If billing account is not part of the same org set `is_org_level` to false. | object({…}) | ✓ | | 0-org-setup | -| [environments](variables-fast.tf#L36) | Long environment names. | object({…}) | ✓ | | 1-resman | +| [environments](variables-fast.tf#L36) | Long environment names. | object({…}) | ✓ | | 0-org-setup | | [prefix](variables-fast.tf#L53) | Prefix used for resources that need unique names. Use a maximum of 9 chars for organizations, and 11 chars for tenants. | string | ✓ | | 0-org-setup | -| [folder_ids](variables-fast.tf#L46) | Folders used by FAST stages in folders/nnnnnnnnnnn format. | map(string) | | {} | 1-resman | +| [folder_ids](variables-fast.tf#L46) | Folders used by FAST stages in folders/nnnnnnnnnnn format. | map(string) | | {} | 0-org-setup | | [iam](variables.tf#L17) | Project-level authoritative IAM bindings for users and service accounts in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | | | [iam_by_principals](variables.tf#L24) | Authoritative IAM binding in {PRINCIPAL => [ROLES]} format. Principals need to be statically defined to avoid cycle errors. Merged internally with the `iam` variable. | map(list(string)) | | {} | | | [network_peerings](variables.tf#L31) | The network peerings between users' VPCs and the VMware Engine networks. Key is used for the peering name suffix. Network is expanded for FAST defined networks. | map(object({…})) | | {…} | | diff --git a/fast/stages/3-gcve-dev/variables-fast.tf b/fast/stages/3-gcve-dev/variables-fast.tf index e8313def6..88d4366e6 100644 --- a/fast/stages/3-gcve-dev/variables-fast.tf +++ b/fast/stages/3-gcve-dev/variables-fast.tf @@ -34,7 +34,7 @@ variable "billing_account" { } variable "environments" { - # tfdoc:variable:source 1-resman + # tfdoc:variable:source 0-org-setup description = "Long environment names." type = object({ dev = object({ @@ -44,7 +44,7 @@ variable "environments" { } variable "folder_ids" { - # tfdoc:variable:source 1-resman + # tfdoc:variable:source 0-org-setup description = "Folders used by FAST stages in folders/nnnnnnnnnnn format." type = map(string) default = {} diff --git a/fast/stages/3-gke-dev/README.md b/fast/stages/3-gke-dev/README.md index 1a523822b..48a8331f0 100644 --- a/fast/stages/3-gke-dev/README.md +++ b/fast/stages/3-gke-dev/README.md @@ -183,13 +183,13 @@ Clusters can then be configured for fleet registration and one of the config man |---|---|:---:|:---:|:---:|:---:| | [automation](variables-fast.tf#L17) | Automation resources created by the bootstrap stage. | object({…}) | ✓ | | 0-org-setup | | [billing_account](variables-fast.tf#L26) | Billing account id. If billing account is not part of the same org set `is_org_level` to false. | object({…}) | ✓ | | 0-org-setup | -| [environments](variables-fast.tf#L34) | Long environment names. | object({…}) | ✓ | | 1-resman | +| [environments](variables-fast.tf#L34) | Long environment names. | object({…}) | ✓ | | 0-org-setup | | [prefix](variables-fast.tf#L60) | Prefix used for resources that need unique names. Use a maximum of 9 chars for organizations, and 11 chars for tenants. | string | ✓ | | 0-org-setup | | [clusters](variables.tf#L17) | Clusters configuration. Refer to the gke-cluster module for type details. | map(object({…})) | | {} | | | [deletion_protection](variables.tf#L102) | Prevent Terraform from destroying data resources. | bool | | false | | | [fleet_config](variables-fleet.tf#L19) | Fleet configuration. | object({…}) | | null | | | [fleet_configmanagement_templates](variables-fleet.tf#L35) | Sets of fleet configurations that can be applied to member clusters, in config name => {options} format. | map(object({…})) | | {} | | -| [folder_ids](variables-fast.tf#L44) | Folder name => id mappings. | map(string) | | {} | 1-resman | +| [folder_ids](variables-fast.tf#L44) | Folder name => id mappings. | map(string) | | {} | 0-org-setup | | [host_project_ids](variables-fast.tf#L52) | Shared VPC host project name => id mappings. | map(string) | | {} | 2-networking | | [iam](variables.tf#L109) | Project-level authoritative IAM bindings for users and service accounts in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | | | [iam_by_principals](variables.tf#L116) | Authoritative IAM binding in {PRINCIPAL => [ROLES]} format. Principals need to be statically defined to avoid cycle errors. Merged internally with the `iam` variable. | map(list(string)) | | {} | | diff --git a/fast/stages/3-gke-dev/variables-fast.tf b/fast/stages/3-gke-dev/variables-fast.tf index 4ca85aeae..39f4f3396 100644 --- a/fast/stages/3-gke-dev/variables-fast.tf +++ b/fast/stages/3-gke-dev/variables-fast.tf @@ -32,7 +32,7 @@ variable "billing_account" { } variable "environments" { - # tfdoc:variable:source 1-resman + # tfdoc:variable:source 0-org-setup description = "Long environment names." type = object({ dev = object({ @@ -42,7 +42,7 @@ variable "environments" { } variable "folder_ids" { - # tfdoc:variable:source 1-resman + # tfdoc:variable:source 0-org-setup description = "Folder name => id mappings." type = map(string) nullable = false diff --git a/fast/stages/3-secops-dev/.fast-stage.env b/fast/stages/3-secops-dev/.fast-stage.env index 1f65dce13..eabb4d26f 100644 --- a/fast/stages/3-secops-dev/.fast-stage.env +++ b/fast/stages/3-secops-dev/.fast-stage.env @@ -1,4 +1,4 @@ FAST_STAGE_DESCRIPTION="SecOps (dev)" FAST_STAGE_LEVEL=3 FAST_STAGE_NAME=secops-dev -FAST_STAGE_DEPS="0-globals 0-org-setup 1-resman 2-secops" +FAST_STAGE_DEPS="0-globals 0-org-setup 2-secops" diff --git a/modules/folder/README.md b/modules/folder/README.md index 56e1a555a..dc3a39ad0 100644 --- a/modules/folder/README.md +++ b/modules/folder/README.md @@ -19,6 +19,7 @@ This module allows the creation and management of folders, including support for - [Custom Security Health Analytics Modules Factory](#custom-security-health-analytics-modules-factory) - [Security Command Center Mute Configs](#security-command-center-mute-configs) - [Security Command Center Mute Configs Factory](#security-command-center-mute-configs-factory) +- [Cloud Asset Search](#cloud-asset-search) - [Cloud Asset Inventory Feeds](#cloud-asset-inventory-feeds) - [Tags](#tags) - [Files](#files) @@ -617,6 +618,30 @@ muteHighSeverity: type: "DYNAMIC" ``` +## Cloud Asset Search + +The Cloud Asset Search feature allows you to search for resources within the project using the Cloud Asset Inventory API. This is useful for discovering and auditing resources based on asset types and query filters. + +```hcl +module "folder" { + source = "./fabric/modules/folder" + billing_account = var.billing_account_id + id = var.folder_id + folder_create = false + asset_search = { + compute-sas = { + asset_types = ["iam.googleapis.com/ServiceAccount"] + query = "name:compute@developer.gserviceaccount.com" + } + } +} + +output "service_accounts" { + value = module.folder.asset_search_results["copute-sas"] +} +# tftest skip +``` + ## Cloud Asset Inventory Feeds Cloud Asset Inventory feeds allow you to monitor asset changes in real-time by publishing notifications to a Pub/Sub topic. Feeds configured at the folder level will monitor all resources within the folder and its subfolders. @@ -709,43 +734,45 @@ module "folder" { | name | description | type | required | default | |---|---|:---:|:---:|:---:| | [asset_feeds](variables.tf#L18) | Cloud Asset Inventory feeds. | map(object({…})) | | {} | -| [assured_workload_config](variables.tf#L51) | Create AssuredWorkloads folder instead of regular folder when value is provided. Incompatible with folder_create=false. | object({…}) | | null | -| [autokey_config](variables.tf#L113) | Enable autokey support for this folder's children. Project accepts either project id or number. | object({…}) | | null | -| [contacts](variables.tf#L122) | List of essential contacts for this resource. Must be in the form EMAIL -> [NOTIFICATION_TYPES]. Valid notification types are ALL, SUSPENSION, SECURITY, TECHNICAL, BILLING, LEGAL, PRODUCT_UPDATES. | map(list(string)) | | {} | -| [context](variables.tf#L141) | Context-specific interpolations. | object({…}) | | {} | -| [deletion_protection](variables.tf#L161) | Deletion protection setting for this folder. | bool | | false | -| [factories_config](variables.tf#L167) | Paths to data files and folders that enable factory functionality. | object({…}) | | {} | -| [firewall_policy](variables.tf#L179) | Hierarchical firewall policy to associate to this folder. | object({…}) | | null | -| [folder_create](variables.tf#L188) | Create folder. When set to false, uses id to reference an existing folder. | bool | | true | +| [asset_search](variables.tf#L51) | Cloud Asset Inventory search configurations. | map(object({…})) | | {} | +| [assured_workload_config](variables.tf#L61) | Create AssuredWorkloads folder instead of regular folder when value is provided. Incompatible with folder_create=false. | object({…}) | | null | +| [autokey_config](variables.tf#L123) | Enable autokey support for this folder's children. Project accepts either project id or number. | object({…}) | | null | +| [contacts](variables.tf#L132) | List of essential contacts for this resource. Must be in the form EMAIL -> [NOTIFICATION_TYPES]. Valid notification types are ALL, SUSPENSION, SECURITY, TECHNICAL, BILLING, LEGAL, PRODUCT_UPDATES. | map(list(string)) | | {} | +| [context](variables.tf#L151) | Context-specific interpolations. | object({…}) | | {} | +| [deletion_protection](variables.tf#L171) | Deletion protection setting for this folder. | bool | | false | +| [factories_config](variables.tf#L177) | Paths to data files and folders that enable factory functionality. | object({…}) | | {} | +| [firewall_policy](variables.tf#L189) | Hierarchical firewall policy to associate to this folder. | object({…}) | | null | +| [folder_create](variables.tf#L198) | Create folder. When set to false, uses id to reference an existing folder. | bool | | true | | [iam](variables-iam.tf#L17) | IAM bindings in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | | [iam_bindings](variables-iam.tf#L24) | Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary. | map(object({…})) | | {} | | [iam_bindings_additive](variables-iam.tf#L39) | Individual additive IAM bindings. Keys are arbitrary. | map(object({…})) | | {} | | [iam_by_principals](variables-iam.tf#L61) | Authoritative IAM binding in {PRINCIPAL => [ROLES]} format. Principals need to be statically defined to avoid errors. Merged internally with the `iam` variable. | map(list(string)) | | {} | | [iam_by_principals_additive](variables-iam.tf#L54) | Additive IAM binding in {PRINCIPAL => [ROLES]} format. Principals need to be statically defined to avoid errors. Merged internally with the `iam_bindings_additive` variable. | map(list(string)) | | {} | | [iam_by_principals_conditional](variables-iam.tf#L68) | Authoritative IAM binding in {PRINCIPAL => {roles = [roles], condition = {cond}}} format. Principals need to be statically defined to avoid errors. Condition is required. | map(object({…})) | | {} | -| [id](variables.tf#L198) | Folder ID in case you use folder_create=false. | string | | null | +| [id](variables.tf#L208) | Folder ID in case you use folder_create=false. | string | | null | | [logging_data_access](variables-logging.tf#L17) | Control activation of data access logs. The special 'allServices' key denotes configuration for all services. | map(object({…})) | | {} | | [logging_exclusions](variables-logging.tf#L28) | Logging exclusions for this folder in the form {NAME -> FILTER}. | map(string) | | {} | | [logging_settings](variables-logging.tf#L35) | Default settings for logging resources. | object({…}) | | null | | [logging_sinks](variables-logging.tf#L45) | Logging sinks to create for the folder. | map(object({…})) | | {} | -| [name](variables.tf#L204) | Folder name. | string | | null | -| [org_policies](variables.tf#L210) | Organization policies applied to this folder keyed by policy name. | map(object({…})) | | {} | +| [name](variables.tf#L214) | Folder name. | string | | null | +| [org_policies](variables.tf#L220) | Organization policies applied to this folder keyed by policy name. | map(object({…})) | | {} | | [pam_entitlements](variables-pam.tf#L17) | Privileged Access Manager entitlements for this resource, keyed by entitlement ID. | map(object({…})) | | {} | -| [parent](variables.tf#L238) | Parent in folders/folder_id or organizations/org_id format. | string | | null | +| [parent](variables.tf#L248) | Parent in folders/folder_id or organizations/org_id format. | string | | null | | [scc_mute_configs](variables-scc.tf#L17) | SCC mute configurations keyed by name. | map(object({…})) | | {} | | [scc_sha_custom_modules](variables-scc.tf#L27) | SCC custom modules keyed by module name. | map(object({…})) | | {} | -| [tag_bindings](variables.tf#L252) | Tag bindings for this folder, in key => tag value id format. | map(string) | | null | +| [tag_bindings](variables.tf#L262) | Tag bindings for this folder, in key => tag value id format. | map(string) | | null | ## Outputs | name | description | sensitive | |---|---|:---:| -| [assured_workload](outputs.tf#L17) | Assured Workloads workload resource. | | -| [folder](outputs.tf#L22) | Folder resource. | | -| [id](outputs.tf#L27) | Fully qualified folder id. | | -| [name](outputs.tf#L38) | Folder name. | | -| [organization_policies_ids](outputs.tf#L47) | Map of ORGANIZATION_POLICIES => ID in the folder. | | -| [scc_custom_sha_modules_ids](outputs.tf#L52) | Map of SCC CUSTOM SHA MODULES => ID in the folder. | | -| [service_agents](outputs.tf#L57) | Identities of all folder-level service agents. | | -| [sink_writer_identities](outputs.tf#L62) | Writer identities created for each sink. | | +| [asset_search_results](outputs.tf#L17) | Cloud Asset Inventory search results. | | +| [assured_workload](outputs.tf#L24) | Assured Workloads workload resource. | | +| [folder](outputs.tf#L29) | Folder resource. | | +| [id](outputs.tf#L34) | Fully qualified folder id. | | +| [name](outputs.tf#L45) | Folder name. | | +| [organization_policies_ids](outputs.tf#L54) | Map of ORGANIZATION_POLICIES => ID in the folder. | | +| [scc_custom_sha_modules_ids](outputs.tf#L59) | Map of SCC CUSTOM SHA MODULES => ID in the folder. | | +| [service_agents](outputs.tf#L64) | Identities of all folder-level service agents. | | +| [sink_writer_identities](outputs.tf#L69) | Writer identities created for each sink. | | diff --git a/modules/folder/assets.tf b/modules/folder/assets.tf index 04bd1b929..94a904f8f 100644 --- a/modules/folder/assets.tf +++ b/modules/folder/assets.tf @@ -14,6 +14,13 @@ * limitations under the License. */ +data "google_cloud_asset_search_all_resources" "default" { + for_each = var.asset_search + scope = local.folder_id + asset_types = each.value.asset_types + query = each.value.query +} + resource "google_cloud_asset_folder_feed" "default" { for_each = var.asset_feeds billing_project = lookup( diff --git a/modules/folder/outputs.tf b/modules/folder/outputs.tf index 1346077d4..b1c14d76d 100644 --- a/modules/folder/outputs.tf +++ b/modules/folder/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2025 Google LLC + * Copyright 2026 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,6 +14,13 @@ * limitations under the License. */ +output "asset_search_results" { + description = "Cloud Asset Inventory search results." + value = { + for k, v in data.google_cloud_asset_search_all_resources.default : k => v.results + } +} + output "assured_workload" { description = "Assured Workloads workload resource." value = try(google_assured_workloads_workload.folder[0], null) diff --git a/modules/folder/variables.tf b/modules/folder/variables.tf index 0de8855cb..d7f3f75f4 100644 --- a/modules/folder/variables.tf +++ b/modules/folder/variables.tf @@ -48,6 +48,16 @@ variable "asset_feeds" { } } +variable "asset_search" { + description = "Cloud Asset Inventory search configurations." + type = map(object({ + asset_types = list(string) + query = optional(string) + })) + default = {} + nullable = false +} + variable "assured_workload_config" { description = "Create AssuredWorkloads folder instead of regular folder when value is provided. Incompatible with folder_create=false." type = object({ diff --git a/modules/gcs/README.md b/modules/gcs/README.md index ec262d8b7..a2f865b73 100644 --- a/modules/gcs/README.md +++ b/modules/gcs/README.md @@ -125,6 +125,31 @@ module "bucket" { # tftest modules=1 resources=1 inventory=retention-logging.yaml ``` +```hcl +module "bucket" { + source = "./fabric/modules/gcs" + project_id = var.project_id + prefix = var.prefix + name = "my-bucket" + location = "EU" + retention_policy = { + retention_period = 100 + is_locked = true + } + soft_delete_retention = 7776000 + context = { + storage_buckets = { + log-bucket = "log-bucket" + } + } + logging_config = { + log_bucket = "$storage_buckets:log-bucket" + log_object_prefix = null + } +} +# tftest modules=1 inventory=retention-logging-context.yaml +``` + ## Lifecycle rule ```hcl @@ -392,42 +417,42 @@ module "bucket" { | name | description | type | required | default | |---|---|:---:|:---:|:---:| -| [name](variables.tf#L223) | Bucket name suffix. | string | ✓ | | +| [name](variables.tf#L224) | Bucket name suffix. | string | ✓ | | | [autoclass](variables.tf#L17) | Enable autoclass to automatically transition objects to appropriate storage classes based on their access pattern. If set to true, storage_class must be set to STANDARD. Defaults to false. | bool | | null | | [bucket_create](variables.tf#L23) | Create bucket. | bool | | true | -| [context](variables.tf#L30) | Context-specific interpolations. | object({…}) | | {} | -| [cors](variables.tf#L45) | CORS configuration for the bucket. Defaults to null. | object({…}) | | null | -| [custom_placement_config](variables.tf#L56) | The bucket's custom location configuration, which specifies the individual regions that comprise a dual-region bucket. If the bucket is designated as REGIONAL or MULTI_REGIONAL, the parameters are empty. | list(string) | | null | -| [default_event_based_hold](variables.tf#L62) | Enable event based hold to new objects added to specific bucket, defaults to false. | bool | | null | -| [enable_hierarchical_namespace](variables.tf#L68) | Enables hierarchical namespace. | bool | | null | -| [enable_object_retention](variables.tf#L74) | Enables object retention on a storage bucket. | bool | | null | -| [encryption_key](variables.tf#L80) | KMS key that will be used for encryption. | string | | null | -| [force_destroy](variables.tf#L86) | Optional map to set force destroy keyed by name, defaults to false. | bool | | false | +| [context](variables.tf#L30) | Context-specific interpolations. | object({…}) | | {} | +| [cors](variables.tf#L46) | CORS configuration for the bucket. Defaults to null. | object({…}) | | null | +| [custom_placement_config](variables.tf#L57) | The bucket's custom location configuration, which specifies the individual regions that comprise a dual-region bucket. If the bucket is designated as REGIONAL or MULTI_REGIONAL, the parameters are empty. | list(string) | | null | +| [default_event_based_hold](variables.tf#L63) | Enable event based hold to new objects added to specific bucket, defaults to false. | bool | | null | +| [enable_hierarchical_namespace](variables.tf#L69) | Enables hierarchical namespace. | bool | | null | +| [enable_object_retention](variables.tf#L75) | Enables object retention on a storage bucket. | bool | | null | +| [encryption_key](variables.tf#L81) | KMS key that will be used for encryption. | string | | null | +| [force_destroy](variables.tf#L87) | Optional map to set force destroy keyed by name, defaults to false. | bool | | false | | [iam](variables-iam.tf#L17) | IAM bindings in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | | [iam_bindings](variables-iam.tf#L23) | Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary. | map(object({…})) | | {} | | [iam_bindings_additive](variables-iam.tf#L38) | Individual additive IAM bindings. Keys are arbitrary. | map(object({…})) | | {} | | [iam_by_principals](variables-iam.tf#L53) | Authoritative IAM binding in {PRINCIPAL => [ROLES]} format. Principals need to be statically defined to avoid cycle errors. Merged internally with the `iam` variable. | map(list(string)) | | {} | -| [ip_filter](variables.tf#L92) | The bucket's IP filter configuration. | object({…}) | | null | -| [kms_autokeys](variables.tf#L103) | KMS Autokey key handles. If location is not specified the bucket location will be used. Key handle names will be added to the kms_keys context with an `autokeys/` prefix. | map(object({…})) | | {} | -| [labels](variables.tf#L121) | Labels to be attached to all buckets. | map(string) | | {} | -| [lifecycle_rules](variables.tf#L127) | Bucket lifecycle rule. | map(object({…})) | | {} | -| [location](variables.tf#L176) | Bucket location. | string | | null | -| [logging_config](variables.tf#L186) | Bucket logging configuration. | object({…}) | | null | -| [managed_folders](variables.tf#L195) | Managed folders to create within the bucket in {PATH => CONFIG} format. | map(object({…})) | | {} | -| [notification_config](variables.tf#L228) | GCS Notification configuration. | object({…}) | | null | -| [objects_to_upload](variables.tf#L246) | Objects to be uploaded to bucket. | map(object({…})) | | {} | -| [prefix](variables.tf#L272) | Optional prefix used to generate the bucket name. | string | | null | -| [project_id](variables.tf#L282) | Bucket project id. Only required when creating buckets, or notification config topics. | string | | null | -| [public_access_prevention](variables.tf#L301) | Prevents public access to the bucket. | string | | null | -| [requester_pays](variables.tf#L311) | Enables Requester Pays on a storage bucket. | bool | | null | -| [retention_policy](variables.tf#L317) | Bucket retention policy. | object({…}) | | null | -| [rpo](variables.tf#L326) | Bucket recovery point objective. | string | | null | -| [soft_delete_retention](variables.tf#L336) | The duration in seconds that soft-deleted objects in the bucket will be retained and cannot be permanently deleted. Set to 0 to override the default and disable. | number | | null | -| [storage_class](variables.tf#L342) | Bucket storage class. | string | | "STANDARD" | -| [tag_bindings](variables.tf#L352) | Tag bindings for this folder, in key => tag value id format. | map(string) | | {} | -| [uniform_bucket_level_access](variables.tf#L359) | Allow using object ACLs (false) or not (true, this is the recommended behavior) , defaults to true (which is the recommended practice, but not the behavior of storage API). | bool | | true | -| [versioning](variables.tf#L365) | Enable versioning, defaults to false. | bool | | null | -| [website](variables.tf#L371) | Bucket website. | object({…}) | | null | +| [ip_filter](variables.tf#L93) | The bucket's IP filter configuration. | object({…}) | | null | +| [kms_autokeys](variables.tf#L104) | KMS Autokey key handles. If location is not specified the bucket location will be used. Key handle names will be added to the kms_keys context with an `autokeys/` prefix. | map(object({…})) | | {} | +| [labels](variables.tf#L122) | Labels to be attached to all buckets. | map(string) | | {} | +| [lifecycle_rules](variables.tf#L128) | Bucket lifecycle rule. | map(object({…})) | | {} | +| [location](variables.tf#L177) | Bucket location. | string | | null | +| [logging_config](variables.tf#L187) | Bucket logging configuration. | object({…}) | | null | +| [managed_folders](variables.tf#L196) | Managed folders to create within the bucket in {PATH => CONFIG} format. | map(object({…})) | | {} | +| [notification_config](variables.tf#L229) | GCS Notification configuration. | object({…}) | | null | +| [objects_to_upload](variables.tf#L247) | Objects to be uploaded to bucket. | map(object({…})) | | {} | +| [prefix](variables.tf#L273) | Optional prefix used to generate the bucket name. | string | | null | +| [project_id](variables.tf#L283) | Bucket project id. Only required when creating buckets, or notification config topics. | string | | null | +| [public_access_prevention](variables.tf#L302) | Prevents public access to the bucket. | string | | null | +| [requester_pays](variables.tf#L312) | Enables Requester Pays on a storage bucket. | bool | | null | +| [retention_policy](variables.tf#L318) | Bucket retention policy. | object({…}) | | null | +| [rpo](variables.tf#L327) | Bucket recovery point objective. | string | | null | +| [soft_delete_retention](variables.tf#L337) | The duration in seconds that soft-deleted objects in the bucket will be retained and cannot be permanently deleted. Set to 0 to override the default and disable. | number | | null | +| [storage_class](variables.tf#L343) | Bucket storage class. | string | | "STANDARD" | +| [tag_bindings](variables.tf#L353) | Tag bindings for this folder, in key => tag value id format. | map(string) | | {} | +| [uniform_bucket_level_access](variables.tf#L360) | Allow using object ACLs (false) or not (true, this is the recommended behavior) , defaults to true (which is the recommended practice, but not the behavior of storage API). | bool | | true | +| [versioning](variables.tf#L366) | Enable versioning, defaults to false. | bool | | null | +| [website](variables.tf#L372) | Bucket website. | object({…}) | | null | ## Outputs diff --git a/modules/gcs/main.tf b/modules/gcs/main.tf index 44f516ed8..25b5b281e 100644 --- a/modules/gcs/main.tf +++ b/modules/gcs/main.tf @@ -125,7 +125,12 @@ resource "google_storage_bucket" "bucket" { dynamic "logging" { for_each = var.logging_config == null ? [] : [""] content { - log_bucket = var.logging_config.log_bucket + # log_bucket is actually a storage bucket (and not a logging bucket) + log_bucket = var.logging_config.log_bucket == null ? null : lookup( + local.ctx.storage_buckets, + var.logging_config.log_bucket, + var.logging_config.log_bucket + ) log_object_prefix = var.logging_config.log_object_prefix } } diff --git a/modules/gcs/variables.tf b/modules/gcs/variables.tf index b10e47a5d..671dd0f35 100644 --- a/modules/gcs/variables.tf +++ b/modules/gcs/variables.tf @@ -30,13 +30,14 @@ variable "bucket_create" { variable "context" { description = "Context-specific interpolations." type = object({ - condition_vars = optional(map(map(string)), {}) - custom_roles = optional(map(string), {}) - iam_principals = optional(map(string), {}) - kms_keys = optional(map(string), {}) - locations = optional(map(string), {}) - project_ids = optional(map(string), {}) - tag_values = optional(map(string), {}) + condition_vars = optional(map(map(string)), {}) + custom_roles = optional(map(string), {}) + iam_principals = optional(map(string), {}) + kms_keys = optional(map(string), {}) + locations = optional(map(string), {}) + project_ids = optional(map(string), {}) + storage_buckets = optional(map(string), {}) + tag_values = optional(map(string), {}) }) default = {} nullable = false diff --git a/modules/organization/README.md b/modules/organization/README.md index 1b8fd2126..f25a33c0f 100644 --- a/modules/organization/README.md +++ b/modules/organization/README.md @@ -33,6 +33,7 @@ To manage organization policies, the `orgpolicy.googleapis.com` service should b - [Custom Security Health Analytics Modules Factory](#custom-security-health-analytics-modules-factory) - [Security Command Center Mute Configs](#security-command-center-mute-configs) - [Security Command Center Mute Configs Factory](#security-command-center-mute-configs-factory) +- [Cloud Asset Search](#cloud-asset-search) - [Cloud Asset Inventory Feeds](#cloud-asset-inventory-feeds) - [Tags](#tags) - [Tags Factory](#tags-factory) @@ -625,6 +626,27 @@ muteHighSeverity: type: "DYNAMIC" ``` +## Cloud Asset Search + +The Cloud Asset Search feature allows you to search for resources within the organization using the Cloud Asset Inventory API. This is useful for discovering and auditing resources based on asset types and query filters. + +```hcl +module "org" { + source = "./fabric/modules/organization" + organization_id = var.organization_id + asset_search = { + org-policies = { + asset_types = ["orgpolicy.googleapis.com/Policy"] + } + } +} + +output "org_policies" { + value = module.org.asset_search_results["org-policies"] +} +# tftest skip +``` + ## Cloud Asset Inventory Feeds Cloud Asset Inventory feeds allow you to monitor asset changes in real-time by publishing notifications to a Pub/Sub topic. Feeds configured at the organization level will monitor all resources within the organization. @@ -910,13 +932,14 @@ module "org" { | name | description | type | required | default | |---|---|:---:|:---:|:---:| -| [organization_id](variables.tf#L162) | Organization id in organizations/nnnnnn format. | string | ✓ | | +| [organization_id](variables.tf#L172) | Organization id in organizations/nnnnnn format. | string | ✓ | | | [asset_feeds](variables.tf#L18) | Cloud Asset Inventory feeds. | map(object({…})) | | {} | -| [contacts](variables.tf#L51) | List of essential contacts for this resource. Must be in the form EMAIL -> [NOTIFICATION_TYPES]. Valid notification types are ALL, SUSPENSION, SECURITY, TECHNICAL, BILLING, LEGAL, PRODUCT_UPDATES. | map(list(string)) | | {} | -| [context](variables.tf#L69) | Context-specific interpolations. | object({…}) | | {} | -| [custom_roles](variables.tf#L89) | Map of role name => list of permissions to create in this project. | map(list(string)) | | {} | -| [factories_config](variables.tf#L96) | Paths to data files and folders that enable factory functionality. | object({…}) | | {} | -| [firewall_policy](variables.tf#L111) | Hierarchical firewall policies to associate to the organization. | object({…}) | | null | +| [asset_search](variables.tf#L51) | Cloud Asset Inventory search configurations. | map(object({…})) | | {} | +| [contacts](variables.tf#L61) | List of essential contacts for this resource. Must be in the form EMAIL -> [NOTIFICATION_TYPES]. Valid notification types are ALL, SUSPENSION, SECURITY, TECHNICAL, BILLING, LEGAL, PRODUCT_UPDATES. | map(list(string)) | | {} | +| [context](variables.tf#L79) | Context-specific interpolations. | object({…}) | | {} | +| [custom_roles](variables.tf#L99) | Map of role name => list of permissions to create in this project. | map(list(string)) | | {} | +| [factories_config](variables.tf#L106) | Paths to data files and folders that enable factory functionality. | object({…}) | | {} | +| [firewall_policy](variables.tf#L121) | Hierarchical firewall policies to associate to the organization. | object({…}) | | null | | [iam](variables-iam.tf#L17) | Authoritative IAM bindings in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | | [iam_bindings](variables-iam.tf#L24) | Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary. | map(object({…})) | | {} | | [iam_bindings_additive](variables-iam.tf#L39) | Individual additive IAM bindings. Keys are arbitrary. | map(object({…})) | | {} | @@ -928,8 +951,8 @@ module "org" { | [logging_settings](variables-logging.tf#L35) | Default settings for logging resources. | object({…}) | | null | | [logging_sinks](variables-logging.tf#L46) | Logging sinks to create for the organization. | map(object({…})) | | {} | | [network_tags](variables-tags.tf#L17) | Network tags by key name. If `id` is provided, key creation is skipped. The `iam` attribute behaves like the similarly named one at module level. | map(object({…})) | | {} | -| [org_policies](variables.tf#L120) | Organization policies applied to this organization keyed by policy name. | map(object({…})) | | {} | -| [org_policy_custom_constraints](variables.tf#L148) | Organization policy custom constraints keyed by constraint name. | map(object({…})) | | {} | +| [org_policies](variables.tf#L130) | Organization policies applied to this organization keyed by policy name. | map(object({…})) | | {} | +| [org_policy_custom_constraints](variables.tf#L158) | Organization policy custom constraints keyed by constraint name. | map(object({…})) | | {} | | [pam_entitlements](variables-pam.tf#L17) | Privileged Access Manager entitlements for this resource, keyed by entitlement ID. | map(object({…})) | | {} | | [scc_mute_configs](variables-scc.tf#L17) | SCC mute configurations keyed by name. | map(object({…})) | | {} | | [scc_sha_custom_modules](variables-scc.tf#L28) | SCC custom modules keyed by module name. | map(object({…})) | | {} | @@ -942,21 +965,22 @@ module "org" { | name | description | sensitive | |---|---|:---:| -| [custom_constraint_ids](outputs.tf#L17) | Map of CUSTOM_CONSTRAINTS => ID in the organization. | | -| [custom_role_id](outputs.tf#L22) | Map of custom role IDs created in the organization. | | -| [custom_roles](outputs.tf#L27) | Map of custom roles resources created in the organization. | | -| [id](outputs.tf#L32) | Fully qualified organization id. | | -| [logging_identities](outputs.tf#L50) | Principals used for logging sinks. | | -| [network_tag_keys](outputs.tf#L62) | Tag key resources. | | -| [network_tag_values](outputs.tf#L71) | Tag value resources. | | -| [organization_id](outputs.tf#L81) | Organization id dependent on module resources. | | -| [organization_policies_ids](outputs.tf#L98) | Map of ORGANIZATION_POLICIES => ID in the organization. | | -| [scc_custom_sha_modules_ids](outputs.tf#L103) | Map of SCC CUSTOM SHA MODULES => ID in the organization. | | -| [scc_mute_configs](outputs.tf#L108) | SCC mute configurations. | | -| [service_agents](outputs.tf#L113) | Identities of all organization-level service agents. | | -| [sink_writer_identities](outputs.tf#L118) | Writer identities created for each sink. | | -| [tag_keys](outputs.tf#L126) | Tag key resources. | | -| [tag_values](outputs.tf#L135) | Tag value resources. | | -| [workforce_identity_provider_names](outputs.tf#L143) | Workforce Identity provider names. | | -| [workforce_identity_providers](outputs.tf#L150) | Workforce Identity provider attributes. | | +| [asset_search_results](outputs.tf#L17) | Cloud Asset Inventory search results. | | +| [custom_constraint_ids](outputs.tf#L24) | Map of CUSTOM_CONSTRAINTS => ID in the organization. | | +| [custom_role_id](outputs.tf#L29) | Map of custom role IDs created in the organization. | | +| [custom_roles](outputs.tf#L34) | Map of custom roles resources created in the organization. | | +| [id](outputs.tf#L39) | Fully qualified organization id. | | +| [logging_identities](outputs.tf#L57) | Principals used for logging sinks. | | +| [network_tag_keys](outputs.tf#L69) | Tag key resources. | | +| [network_tag_values](outputs.tf#L78) | Tag value resources. | | +| [organization_id](outputs.tf#L88) | Organization id dependent on module resources. | | +| [organization_policies_ids](outputs.tf#L105) | Map of ORGANIZATION_POLICIES => ID in the organization. | | +| [scc_custom_sha_modules_ids](outputs.tf#L110) | Map of SCC CUSTOM SHA MODULES => ID in the organization. | | +| [scc_mute_configs](outputs.tf#L115) | SCC mute configurations. | | +| [service_agents](outputs.tf#L120) | Identities of all organization-level service agents. | | +| [sink_writer_identities](outputs.tf#L125) | Writer identities created for each sink. | | +| [tag_keys](outputs.tf#L133) | Tag key resources. | | +| [tag_values](outputs.tf#L142) | Tag value resources. | | +| [workforce_identity_provider_names](outputs.tf#L150) | Workforce Identity provider names. | | +| [workforce_identity_providers](outputs.tf#L157) | Workforce Identity provider attributes. | | diff --git a/modules/organization/assets.tf b/modules/organization/assets.tf index 59a650f5e..ea1727e00 100644 --- a/modules/organization/assets.tf +++ b/modules/organization/assets.tf @@ -14,6 +14,13 @@ * limitations under the License. */ +data "google_cloud_asset_search_all_resources" "default" { + for_each = var.asset_search + scope = var.organization_id + asset_types = each.value.asset_types + query = each.value.query +} + resource "google_cloud_asset_organization_feed" "default" { for_each = var.asset_feeds billing_project = lookup( diff --git a/modules/organization/outputs.tf b/modules/organization/outputs.tf index a77041b2e..3eec6ceea 100644 --- a/modules/organization/outputs.tf +++ b/modules/organization/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2025 Google LLC + * Copyright 2026 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,6 +14,13 @@ * limitations under the License. */ +output "asset_search_results" { + description = "Cloud Asset Inventory search results." + value = { + for k, v in data.google_cloud_asset_search_all_resources.default : k => v.results + } +} + output "custom_constraint_ids" { description = "Map of CUSTOM_CONSTRAINTS => ID in the organization." value = { for k, v in google_org_policy_custom_constraint.constraint : k => v.id } diff --git a/modules/organization/variables.tf b/modules/organization/variables.tf index ac814cac0..03ce35388 100644 --- a/modules/organization/variables.tf +++ b/modules/organization/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2025 Google LLC + * Copyright 2026 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -48,6 +48,16 @@ variable "asset_feeds" { } } +variable "asset_search" { + description = "Cloud Asset Inventory search configurations." + type = map(object({ + asset_types = list(string) + query = optional(string) + })) + default = {} + nullable = false +} + variable "contacts" { description = "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." type = map(list(string)) diff --git a/modules/project-factory/README.md b/modules/project-factory/README.md index 91e3df336..c5be4d194 100644 --- a/modules/project-factory/README.md +++ b/modules/project-factory/README.md @@ -424,6 +424,9 @@ module "project-factory" { pubsub_topics = { feeds-topic = "projects/my-cai-feeds-project/topics/feed" } + storage_buckets = { + log-bucket = "log-bucket" + } tag_values = { "context/gke" = "tagValues/654321" "org-policies/drs-allow-all" = "tagValues/123456" @@ -666,6 +669,14 @@ shared_vpc_service_config: - $service_agents:container-engine billing_budgets: - $billing_budgets:test-100 +buckets: + app-0-bucket-a: + location: europe-west8 + app-0-bucket-b: + location: europe-west8 + logging_config: + log_bucket: $storage_buckets:log-bucket + log_object_prefix: log-prefix pam_entitlements: project-admins: max_request_duration: 3600s @@ -752,6 +763,9 @@ automation: - group:team-b-admins@example.org - $iam_principals:service_accounts/dev-tb-app0-0/automation/rw - $iam_principals:service_accounts/dev-tb-app0-0/automation/ro + logging_config: + log_bucket: $storage_buckets:log-bucket + log_object_prefix: log-prefix # tftest-file id=7 path=data/projects/dev-tb-app0-0.yaml schema=project.schema.json ``` @@ -841,14 +855,14 @@ compute.disableSerialPortAccess: | name | description | type | required | default | |---|---|:---:|:---:|:---:| -| [factories_config](variables.tf#L163) | Path to folder with YAML resource description data files. | object({…}) | ✓ | | -| [context](variables.tf#L17) | Context-specific interpolations. | object({…}) | | {} | -| [data_defaults](variables.tf#L40) | Optional default values used when corresponding project or folder data from files are missing. | object({…}) | | {} | -| [data_merges](variables.tf#L105) | Optional values that will be merged with corresponding data from files. Combines with `data_defaults`, file data, and `data_overrides`. | object({…}) | | {} | -| [data_overrides](variables.tf#L124) | Optional values that override corresponding data from files. Takes precedence over file data and `data_defaults`. | object({…}) | | {} | +| [factories_config](variables.tf#L164) | Path to folder with YAML resource description data files. | object({…}) | ✓ | | +| [context](variables.tf#L17) | Context-specific interpolations. | object({…}) | | {} | +| [data_defaults](variables.tf#L41) | Optional default values used when corresponding project or folder data from files are missing. | object({…}) | | {} | +| [data_merges](variables.tf#L106) | Optional values that will be merged with corresponding data from files. Combines with `data_defaults`, file data, and `data_overrides`. | object({…}) | | {} | +| [data_overrides](variables.tf#L125) | Optional values that override corresponding data from files. Takes precedence over file data and `data_defaults`. | object({…}) | | {} | | [folders](variables-folders.tf#L17) | Folders data merged with factory data. | map(object({…})) | | {} | | [notification_channels](variables-billing.tf#L17) | Notification channels used by budget alerts. | map(object({…})) | | {} | -| [projects](variables-projects.tf#L17) | Projects data merged with factory data. | map(object({…})) | | {} | +| [projects](variables-projects.tf#L17) | Projects data merged with factory data. | map(object({…})) | | {} | ## Outputs @@ -868,6 +882,7 @@ compute.disableSerialPortAccess: | [service_accounts](outputs.tf#L158) | Service account emails. | | | [storage_buckets](outputs.tf#L163) | Bucket names. | | + ## Tests These tests validate fixes to the project factory. @@ -875,6 +890,13 @@ These tests validate fixes to the project factory. ```hcl module "project-factory" { source = "./fabric/modules/project-factory" + context = { + condition_vars = { + organization = { + id = 1234567890 + } + } + } data_defaults = { billing_account = "012345-67890A-ABCDEF" locations = { @@ -894,7 +916,7 @@ module "project-factory" { projects = "data/projects" } } -# tftest modules=4 resources=24 files=test-0,test-1,test-2 inventory=test-1.yaml +# tftest modules=5 resources=29 files=test-0,test-1,test-2 inventory=test-1.yaml ``` ```yaml @@ -904,6 +926,22 @@ services: - iam.googleapis.com - contactcenteraiplatform.googleapis.com - container.googleapis.com +iam_bindings_additive: + test_context: + role: roles/viewer + member: user:user1@example.com + condition: + title: Test context + expression: resource.matchTag('${organization.id}/context', 'project-factory') +tags: + allow-key-creation: + description: Allow key creation for automation service account + values: + allow: + description: Allow key creation + iam: + roles/resourcemanager.tagUser: + - $iam_principals:service_accounts/tags-iam-test/automation/rw # tftest-file id=test-0 path=data/projects/test-0.yaml ``` @@ -915,6 +953,8 @@ prefix: null services: - iam.googleapis.com - contactcenteraiplatform.googleapis.com +tag_bindings: + test: $tag_values/ # tftest-file id=test-1 path=data/projects/test-1.yaml ``` diff --git a/modules/project-factory/automation.tf b/modules/project-factory/automation.tf index d0496f966..a0be4a17a 100644 --- a/modules/project-factory/automation.tf +++ b/modules/project-factory/automation.tf @@ -46,6 +46,7 @@ locals { lifecycle_rules = lookup(v.bucket, "lifecycle_rules", {}) retention_policy = lookup(v.bucket, "retention_policy", null) soft_delete_retention = lookup(v.bucket, "soft_delete_retention", null) + logging_config = lookup(v.bucket, "logging_config", null) prefix = try(coalesce( local.data_defaults.overrides.prefix, v.prefix, @@ -95,8 +96,9 @@ module "automation-bucket" { local.data_defaults.defaults.force_destroy, ), null) context = merge(local.ctx, { - project_ids = local.ctx_project_ids - iam_principals = local.ctx_iam_principals + project_ids = local.ctx_project_ids + iam_principals = local.ctx_iam_principals + storage_buckets = local.ctx.storage_buckets }) iam = lookup(each.value, "iam", {}) iam_bindings = lookup(each.value, "iam_bindings", {}) @@ -120,6 +122,7 @@ module "automation-bucket" { ) retention_policy = each.value.retention_policy soft_delete_retention = each.value.soft_delete_retention + logging_config = each.value.logging_config } module "automation-service-accounts" { diff --git a/modules/project-factory/main.tf b/modules/project-factory/main.tf index 90d85bf22..55ac766d1 100644 --- a/modules/project-factory/main.tf +++ b/modules/project-factory/main.tf @@ -39,7 +39,7 @@ resource "terraform_data" "defaults_preconditions" { } # precondition { # condition = local.projects_input == null - # error_message = yamlencode(local.budget_project_sets) + # error_message = yamlencode(var.context.condition_vars) # } } } diff --git a/modules/project-factory/projects-buckets.tf b/modules/project-factory/projects-buckets.tf index 9244bf305..bffb78fd1 100644 --- a/modules/project-factory/projects-buckets.tf +++ b/modules/project-factory/projects-buckets.tf @@ -53,6 +53,7 @@ locals { retention_policy = lookup(opts, "retention_policy", null) soft_delete_retention = lookup(opts, "soft_delete_retention", null) lifecycle_rules = lookup(opts, "lifecycle_rules", {}) + logging_config = lookup(opts, "logging_config", null) enable_object_retention = lookup(opts, "enable_object_retention", null) } ] @@ -77,9 +78,10 @@ module "buckets" { local.automation_sas_iam_emails, lookup(local.self_sas_iam_emails, each.value.project_key, {}) ) - kms_keys = merge(local.ctx.kms_keys, local.kms_keys, local.kms_autokeys) - locations = local.ctx.locations - project_ids = local.ctx_project_ids + kms_keys = merge(local.ctx.kms_keys, local.kms_keys, local.kms_autokeys) + locations = local.ctx.locations + project_ids = local.ctx_project_ids + storage_buckets = local.ctx.storage_buckets }) iam = each.value.iam iam_bindings = each.value.iam_bindings @@ -98,5 +100,6 @@ module "buckets" { versioning = each.value.versioning retention_policy = each.value.retention_policy soft_delete_retention = each.value.soft_delete_retention + logging_config = each.value.logging_config enable_object_retention = each.value.enable_object_retention } diff --git a/modules/project-factory/projects.tf b/modules/project-factory/projects.tf index 1366b9c8b..3d17037d9 100644 --- a/modules/project-factory/projects.tf +++ b/modules/project-factory/projects.tf @@ -95,11 +95,11 @@ module "projects" { each.value.contacts, var.data_merges.contacts ) context = merge(local.ctx, { - condition_vars = { + condition_vars = merge(local.ctx.condition_vars, { folder_ids = { for k, v in local.ctx_folder_ids : replace(k, "$folder_ids:", "") => v } - } + }) folder_ids = local.ctx_folder_ids }) default_service_account = try(each.value.default_service_account, "keep") diff --git a/modules/project-factory/schemas/project.schema.json b/modules/project-factory/schemas/project.schema.json index 87866ff86..4881ece7c 100644 --- a/modules/project-factory/schemas/project.schema.json +++ b/modules/project-factory/schemas/project.schema.json @@ -1121,6 +1121,21 @@ } } }, + "logging_config": { + "type": "object", + "additionalProperties": false, + "properties": { + "log_bucket": { + "type": "string" + }, + "log_object_prefix": { + "type": "string" + } + }, + "required": [ + "log_bucket" + ] + }, "location": { "type": "string" }, diff --git a/modules/project-factory/schemas/project.schema.md b/modules/project-factory/schemas/project.schema.md index f4b2057e6..5e7f57c9b 100644 --- a/modules/project-factory/schemas/project.schema.md +++ b/modules/project-factory/schemas/project.schema.md @@ -308,6 +308,10 @@ - **num_newer_versions**: *number* - **with_state**: *string*
*enum: ['LIVE', 'ARCHIVED', 'ANY']* + - **logging_config**: *object* +
*additional properties: false* + - **log_bucket**: *string* + - **log_object_prefix**: *string* - **location**: *string* - **managed_folders**: *object*
*additional properties: false* diff --git a/modules/project-factory/variables-projects.tf b/modules/project-factory/variables-projects.tf index d63c7d2e5..88f69a388 100644 --- a/modules/project-factory/variables-projects.tf +++ b/modules/project-factory/variables-projects.tf @@ -105,6 +105,10 @@ variable "projects" { with_state = optional(string) }) })), {}) + logging_config = optional(object({ + log_bucket = string + log_object_prefix = optional(string) + }), null) retention_policy = optional(object({ retention_period = string is_locked = optional(bool) @@ -211,6 +215,10 @@ variable "projects" { with_state = optional(string) }) })), {}) + logging_config = optional(object({ + log_bucket = string + log_object_prefix = optional(string) + }), null) retention_policy = optional(object({ retention_period = string is_locked = optional(bool) diff --git a/modules/project-factory/variables.tf b/modules/project-factory/variables.tf index f9e68249f..b387ff686 100644 --- a/modules/project-factory/variables.tf +++ b/modules/project-factory/variables.tf @@ -29,6 +29,7 @@ variable "context" { project_ids = optional(map(string), {}) project_numbers = optional(map(string), {}) pubsub_topics = optional(map(string), {}) + storage_buckets = optional(map(string), {}) tag_values = optional(map(string), {}) vpc_host_projects = optional(map(string), {}) vpc_sc_perimeters = optional(map(string), {}) diff --git a/modules/project/README.md b/modules/project/README.md index a28ac5213..ae5ac7b1d 100644 --- a/modules/project/README.md +++ b/modules/project/README.md @@ -39,6 +39,7 @@ This module implements the creation and management of one GCP project including - [Privileged Access Manager (PAM) Entitlements Factory](#privileged-access-manager-pam-entitlements-factory) - [VPC Service Controls](#vpc-service-controls) - [Default compute network tier](#default-compute-network-tier) +- [Cloud Asset Search](#cloud-asset-search) - [Cloud Asset Inventory Feeds](#cloud-asset-inventory-feeds) - [BigQuery Reservations](#bigquery-reservations) - [Project Related Outputs](#project-related-outputs) @@ -1641,6 +1642,31 @@ module "project" { # tftest modules=1 resources=4 ``` +## Cloud Asset Search + +The Cloud Asset Search feature allows you to search for resources within the project using the Cloud Asset Inventory API. This is useful for discovering and auditing resources based on asset types and query filters. + +```hcl +module "project" { + source = "./fabric/modules/project" + billing_account = var.billing_account_id + name = "project" + parent = var.folder_id + prefix = var.prefix + asset_search = { + compute-sas = { + asset_types = ["iam.googleapis.com/ServiceAccount"] + query = "name:compute@developer.gserviceaccount.com" + } + } +} + +output "service_accounts" { + value = module.project.asset_search_results["copute-sas"] +} +# tftest skip +``` + ## Cloud Asset Inventory Feeds Cloud Asset Inventory feeds allow you to monitor asset changes in real-time by publishing notifications to a Pub/Sub topic. Feeds can be configured to monitor specific asset types, filter by conditions, and export different content types. @@ -2258,30 +2284,31 @@ module "project" { | name | description | type | required | default | |---|---|:---:|:---:|:---:| -| [name](variables.tf#L238) | Project name and id suffix. | string | ✓ | | +| [name](variables.tf#L248) | Project name and id suffix. | string | ✓ | | | [alerts](variables-observability.tf#L17) | Monitoring alerts. | map(object({…})) | | {} | | [asset_feeds](variables.tf#L18) | Cloud Asset Inventory feeds. | map(object({…})) | | {} | -| [auto_create_network](variables.tf#L51) | Whether to create the default network for the project. | bool | | false | -| [bigquery_reservations](variables.tf#L57) | BigQuery reservations and assignments. Assignment specified as {JOB_TYPE = ['projects/PROJECT_ID']}. | map(object({…})) | | {} | -| [billing_account](variables.tf#L94) | Billing account id. | string | | null | -| [compute_metadata](variables.tf#L100) | Optional compute metadata key/values. Only usable if compute API has been enabled. | map(string) | | {} | -| [contacts](variables.tf#L107) | List of essential contacts for this resource. Must be in the form EMAIL -> [NOTIFICATION_TYPES]. Valid notification types are ALL, SUSPENSION, SECURITY, TECHNICAL, BILLING, LEGAL, PRODUCT_UPDATES. | map(list(string)) | | {} | -| [context](variables.tf#L125) | Context-specific interpolations. | object({…}) | | {} | -| [custom_roles](variables.tf#L148) | Map of role name => list of permissions to create in this project. | map(list(string)) | | {} | -| [default_network_tier](variables.tf#L155) | Default compute network tier for the project. | string | | null | -| [default_service_account](variables.tf#L161) | Project default service account setting: can be one of `delete`, `deprivilege`, `disable`, or `keep`. | string | | "keep" | -| [deletion_policy](variables.tf#L174) | Deletion policy setting for this project. | string | | "DELETE" | -| [descriptive_name](variables.tf#L185) | Descriptive project name. Set when name differs from project id. | string | | null | -| [factories_config](variables.tf#L191) | Paths to data files and folders that enable factory functionality. | object({…}) | | {} | +| [asset_search](variables.tf#L51) | Cloud Asset Inventory search configurations. | map(object({…})) | | {} | +| [auto_create_network](variables.tf#L61) | Whether to create the default network for the project. | bool | | false | +| [bigquery_reservations](variables.tf#L67) | BigQuery reservations and assignments. Assignment specified as {JOB_TYPE = ['projects/PROJECT_ID']}. | map(object({…})) | | {} | +| [billing_account](variables.tf#L104) | Billing account id. | string | | null | +| [compute_metadata](variables.tf#L110) | Optional compute metadata key/values. Only usable if compute API has been enabled. | map(string) | | {} | +| [contacts](variables.tf#L117) | List of essential contacts for this resource. Must be in the form EMAIL -> [NOTIFICATION_TYPES]. Valid notification types are ALL, SUSPENSION, SECURITY, TECHNICAL, BILLING, LEGAL, PRODUCT_UPDATES. | map(list(string)) | | {} | +| [context](variables.tf#L135) | Context-specific interpolations. | object({…}) | | {} | +| [custom_roles](variables.tf#L158) | Map of role name => list of permissions to create in this project. | map(list(string)) | | {} | +| [default_network_tier](variables.tf#L165) | Default compute network tier for the project. | string | | null | +| [default_service_account](variables.tf#L171) | Project default service account setting: can be one of `delete`, `deprivilege`, `disable`, or `keep`. | string | | "keep" | +| [deletion_policy](variables.tf#L184) | Deletion policy setting for this project. | string | | "DELETE" | +| [descriptive_name](variables.tf#L195) | Descriptive project name. Set when name differs from project id. | string | | null | +| [factories_config](variables.tf#L201) | Paths to data files and folders that enable factory functionality. | object({…}) | | {} | | [iam](variables-iam.tf#L17) | Authoritative IAM bindings in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | | [iam_bindings](variables-iam.tf#L24) | Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary. | map(object({…})) | | {} | | [iam_bindings_additive](variables-iam.tf#L39) | Individual additive IAM bindings. Keys are arbitrary. | map(object({…})) | | {} | | [iam_by_principals](variables-iam.tf#L61) | Authoritative IAM binding in {PRINCIPAL => [ROLES]} format. Principals need to be statically defined to avoid errors. Merged internally with the `iam` variable. | map(list(string)) | | {} | | [iam_by_principals_additive](variables-iam.tf#L54) | Additive IAM binding in {PRINCIPAL => [ROLES]} format. Principals need to be statically defined to avoid errors. Merged internally with the `iam_bindings_additive` variable. | map(list(string)) | | {} | | [iam_by_principals_conditional](variables-iam.tf#L68) | Authoritative IAM binding in {PRINCIPAL => {roles = [roles], condition = {cond}}} format. Principals need to be statically defined to avoid errors. Condition is required. | map(object({…})) | | {} | -| [kms_autokeys](variables.tf#L207) | KMS Autokey key handles. | map(object({…})) | | {} | -| [labels](variables.tf#L225) | Resource labels. | map(string) | | {} | -| [lien_reason](variables.tf#L232) | If non-empty, creates a project lien with this description. | string | | null | +| [kms_autokeys](variables.tf#L217) | KMS Autokey key handles. | map(object({…})) | | {} | +| [labels](variables.tf#L235) | Resource labels. | map(string) | | {} | +| [lien_reason](variables.tf#L242) | If non-empty, creates a project lien with this description. | string | | null | | [log_scopes](variables-observability.tf#L117) | Log scopes under this project. | map(object({…})) | | {} | | [logging_data_access](variables-observability.tf#L127) | Control activation of data access logs. The special 'allServices' key denotes configuration for all services. | map(object({…})) | | {} | | [logging_exclusions](variables-observability.tf#L138) | Logging exclusions for this project in the form {NAME -> FILTER}. | map(string) | | {} | @@ -2290,26 +2317,26 @@ module "project" { | [metric_scopes](variables-observability.tf#L216) | List of projects that will act as metric scopes for this project. | list(string) | | [] | | [network_tags](variables-tags.tf#L17) | Network tags by key name. If `id` is provided, key creation is skipped. The `iam` attribute behaves like the similarly named one at module level. | map(object({…})) | | {} | | [notification_channels](variables-observability.tf#L223) | Monitoring notification channels. | map(object({…})) | | {} | -| [org_policies](variables.tf#L243) | Organization policies applied to this project keyed by policy name. | map(object({…})) | | {} | +| [org_policies](variables.tf#L253) | Organization policies applied to this project keyed by policy name. | map(object({…})) | | {} | | [pam_entitlements](variables-pam.tf#L17) | Privileged Access Manager entitlements for this resource, keyed by entitlement ID. | map(object({…})) | | {} | -| [parent](variables.tf#L271) | Parent folder or organization in 'folders/folder_id' or 'organizations/org_id' format. | string | | null | -| [prefix](variables.tf#L285) | Optional prefix used to generate project id and name. | string | | null | -| [project_reuse](variables.tf#L295) | Reuse existing project if not null. If name and number are not passed in, a data source is used. | object({…}) | | null | +| [parent](variables.tf#L281) | Parent folder or organization in 'folders/folder_id' or 'organizations/org_id' format. | string | | null | +| [prefix](variables.tf#L295) | Optional prefix used to generate project id and name. | string | | null | +| [project_reuse](variables.tf#L305) | Reuse existing project if not null. If name and number are not passed in, a data source is used. | object({…}) | | null | | [quotas](variables-quotas.tf#L17) | Service quota configuration. | map(object({…})) | | {} | | [scc_mute_configs](variables-scc.tf#L17) | SCC mute configurations keyed by name. | map(object({…})) | | {} | | [scc_sha_custom_modules](variables-scc.tf#L28) | SCC custom modules keyed by module name. | map(object({…})) | | {} | -| [service_agents_config](variables.tf#L315) | Automatic service agent configuration options. | object({…}) | | {} | -| [service_config](variables.tf#L326) | Configure service API activation. | object({…}) | | {…} | -| [service_encryption_key_ids](variables.tf#L338) | Service Agents to be granted encryption/decryption permissions over Cloud KMS encryption keys. Format {SERVICE_AGENT => [KEY_ID]}. | map(list(string)) | | {} | -| [services](variables.tf#L345) | Service APIs to enable. | list(string) | | [] | -| [shared_vpc_host_config](variables.tf#L351) | Configures this project as a Shared VPC host project (mutually exclusive with shared_vpc_service_project). | object({…}) | | null | -| [shared_vpc_service_config](variables.tf#L361) | Configures this project as a Shared VPC service project (mutually exclusive with shared_vpc_host_config). | object({…}) | | {…} | -| [skip_delete](variables.tf#L398) | Deprecated. Use deletion_policy. | bool | | null | +| [service_agents_config](variables.tf#L325) | Automatic service agent configuration options. | object({…}) | | {} | +| [service_config](variables.tf#L336) | Configure service API activation. | object({…}) | | {…} | +| [service_encryption_key_ids](variables.tf#L348) | Service Agents to be granted encryption/decryption permissions over Cloud KMS encryption keys. Format {SERVICE_AGENT => [KEY_ID]}. | map(list(string)) | | {} | +| [services](variables.tf#L355) | Service APIs to enable. | list(string) | | [] | +| [shared_vpc_host_config](variables.tf#L361) | Configures this project as a Shared VPC host project (mutually exclusive with shared_vpc_service_project). | object({…}) | | null | +| [shared_vpc_service_config](variables.tf#L371) | Configures this project as a Shared VPC service project (mutually exclusive with shared_vpc_host_config). | object({…}) | | {…} | +| [skip_delete](variables.tf#L408) | Deprecated. Use deletion_policy. | bool | | null | | [tag_bindings](variables-tags.tf#L89) | Tag bindings for this project, in key => tag value id format. | map(string) | | null | | [tags](variables-tags.tf#L96) | Tags by key name. If `id` is provided, key or value creation is skipped. The `iam` attribute behaves like the similarly named one at module level. | map(object({…})) | | {} | | [tags_config](variables-tags.tf#L161) | Fine-grained control on tag resource and IAM creation. | object({…}) | | {} | -| [universe](variables.tf#L410) | GCP universe where to deploy the project. The prefix will be prepended to the project id. | object({…}) | | null | -| [vpc_sc](variables.tf#L421) | VPC-SC configuration for the project, use when `ignore_changes` for resources is set in the VPC-SC module. | object({…}) | | null | +| [universe](variables.tf#L420) | GCP universe where to deploy the project. The prefix will be prepended to the project id. | object({…}) | | null | +| [vpc_sc](variables.tf#L431) | VPC-SC configuration for the project, use when `ignore_changes` for resources is set in the VPC-SC module. | object({…}) | | null | | [workload_identity_pools](variables-identity-providers.tf#L17) | Workload Identity Federation pools and providers. | map(object({…})) | | {} | ## Outputs @@ -2317,31 +2344,32 @@ module "project" { | name | description | sensitive | |---|---|:---:| | [alert_ids](outputs.tf#L17) | Monitoring alert IDs. | | -| [bigquery_reservations](outputs.tf#L25) | BigQuery reservations and assignments. | | -| [custom_role_id](outputs.tf#L33) | Map of custom role IDs created in the project. | | -| [custom_roles](outputs.tf#L38) | Map of custom roles resources created in the project. | | -| [default_service_accounts](outputs.tf#L43) | Emails of the default service accounts for this project. | | -| [id](outputs.tf#L48) | Project id. | | -| [kms_autokeys](outputs.tf#L66) | KMS Autokey key ids. | | -| [name](outputs.tf#L73) | Project name. | | -| [network_tag_keys](outputs.tf#L85) | Tag key resources. | | -| [network_tag_values](outputs.tf#L94) | Tag value resources. | | -| [notification_channel_names](outputs.tf#L102) | Notification channel names. | | -| [notification_channels](outputs.tf#L110) | Full notification channel objects. | | -| [number](outputs.tf#L115) | Project number. | | -| [organization_policies_ids](outputs.tf#L130) | Map of ORGANIZATION_POLICIES => ID in the organization. | | -| [project_id](outputs.tf#L137) | Project id. | | -| [quota_configs](outputs.tf#L155) | Quota configurations. | | -| [quotas](outputs.tf#L166) | Quota resources. | | -| [scc_custom_sha_modules_ids](outputs.tf#L171) | Map of SCC CUSTOM SHA MODULES => ID in the project. | | -| [service_agents](outputs.tf#L176) | List of all (active) service agents for this project. | | -| [services](outputs.tf#L185) | Service APIs to enable in the project. | | -| [sink_writer_identities](outputs.tf#L194) | Writer identities created for each sink. | | -| [tag_keys](outputs.tf#L201) | Tag key resources. | | -| [tag_values](outputs.tf#L210) | Tag value resources. | | -| [workload_identity_pool_ids](outputs.tf#L218) | Workload identity provider ids. | | -| [workload_identity_provider_ids](outputs.tf#L225) | Workload identity provider attributes. | | -| [workload_identity_providers](outputs.tf#L233) | Workload identity provider attributes. | | +| [asset_search_results](outputs.tf#L25) | Cloud Asset Inventory search results. | | +| [bigquery_reservations](outputs.tf#L32) | BigQuery reservations and assignments. | | +| [custom_role_id](outputs.tf#L40) | Map of custom role IDs created in the project. | | +| [custom_roles](outputs.tf#L45) | Map of custom roles resources created in the project. | | +| [default_service_accounts](outputs.tf#L50) | Emails of the default service accounts for this project. | | +| [id](outputs.tf#L55) | Project id. | | +| [kms_autokeys](outputs.tf#L73) | KMS Autokey key ids. | | +| [name](outputs.tf#L80) | Project name. | | +| [network_tag_keys](outputs.tf#L92) | Tag key resources. | | +| [network_tag_values](outputs.tf#L101) | Tag value resources. | | +| [notification_channel_names](outputs.tf#L109) | Notification channel names. | | +| [notification_channels](outputs.tf#L117) | Full notification channel objects. | | +| [number](outputs.tf#L122) | Project number. | | +| [organization_policies_ids](outputs.tf#L137) | Map of ORGANIZATION_POLICIES => ID in the organization. | | +| [project_id](outputs.tf#L144) | Project id. | | +| [quota_configs](outputs.tf#L162) | Quota configurations. | | +| [quotas](outputs.tf#L173) | Quota resources. | | +| [scc_custom_sha_modules_ids](outputs.tf#L178) | Map of SCC CUSTOM SHA MODULES => ID in the project. | | +| [service_agents](outputs.tf#L183) | List of all (active) service agents for this project. | | +| [services](outputs.tf#L192) | Service APIs to enable in the project. | | +| [sink_writer_identities](outputs.tf#L201) | Writer identities created for each sink. | | +| [tag_keys](outputs.tf#L208) | Tag key resources. | | +| [tag_values](outputs.tf#L217) | Tag value resources. | | +| [workload_identity_pool_ids](outputs.tf#L225) | Workload identity provider ids. | | +| [workload_identity_provider_ids](outputs.tf#L232) | Workload identity provider attributes. | | +| [workload_identity_providers](outputs.tf#L240) | Workload identity provider attributes. | | ## Fixtures diff --git a/modules/project/assets.tf b/modules/project/assets.tf index 77fac7cab..8d8dfb6dc 100644 --- a/modules/project/assets.tf +++ b/modules/project/assets.tf @@ -14,6 +14,13 @@ * limitations under the License. */ +data "google_cloud_asset_search_all_resources" "default" { + for_each = var.asset_search + scope = "projects/${local.project.project_id}" + asset_types = each.value.asset_types + query = each.value.query +} + resource "google_cloud_asset_project_feed" "default" { for_each = var.asset_feeds project = local.project.project_id diff --git a/modules/project/outputs.tf b/modules/project/outputs.tf index 1fa7b32b8..aae95dd83 100644 --- a/modules/project/outputs.tf +++ b/modules/project/outputs.tf @@ -22,6 +22,13 @@ output "alert_ids" { } } +output "asset_search_results" { + description = "Cloud Asset Inventory search results." + value = { + for k, v in data.google_cloud_asset_search_all_resources.default : k => v.results + } +} + output "bigquery_reservations" { description = "BigQuery reservations and assignments." value = { diff --git a/modules/project/variables.tf b/modules/project/variables.tf index 22471b1ea..8a4d495f3 100644 --- a/modules/project/variables.tf +++ b/modules/project/variables.tf @@ -48,6 +48,16 @@ variable "asset_feeds" { } } +variable "asset_search" { + description = "Cloud Asset Inventory search configurations." + type = map(object({ + asset_types = list(string) + query = optional(string) + })) + default = {} + nullable = false +} + variable "auto_create_network" { description = "Whether to create the default network for the project." type = bool diff --git a/tests/modules/gcs/examples/retention-logging-context.yaml b/tests/modules/gcs/examples/retention-logging-context.yaml new file mode 100644 index 000000000..9c869849b --- /dev/null +++ b/tests/modules/gcs/examples/retention-logging-context.yaml @@ -0,0 +1,28 @@ +# Copyright 2025 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.bucket.google_storage_bucket.bucket[0]: + logging: + - log_bucket: log-bucket + name: test-my-bucket + project: project-id + soft_delete_policy: + - retention_duration_seconds: 7776000 + retention_policy: + - is_locked: true + retention_period: "100" + +counts: + google_storage_bucket: 1 diff --git a/tests/modules/project_factory/examples/example.yaml b/tests/modules/project_factory/examples/example.yaml index 01337b178..c4d1d307f 100644 --- a/tests/modules/project_factory/examples/example.yaml +++ b/tests/modules/project_factory/examples/example.yaml @@ -30,7 +30,9 @@ values: labels: null lifecycle_rule: [] location: EU - logging: [] + logging: + - log_bucket: log-bucket + log_object_prefix: log-prefix name: test-pf-dev-tb-app0-0-tf-state project: test-pf-teams-iac-0 requester_pays: null @@ -119,6 +121,62 @@ values: timeouts: null type: email user_labels: null + module.project-factory.module.buckets["dev-ta-app0-be/app-0-bucket-a"].google_storage_bucket.bucket[0]: + autoclass: [] + cors: [] + custom_placement_config: [] + default_event_based_hold: null + effective_labels: + goog-terraform-provisioned: 'true' + enable_object_retention: null + encryption: [] + force_destroy: false + hierarchical_namespace: [] + ip_filter: [] + labels: null + lifecycle_rule: [] + location: EUROPE-WEST8 + logging: [] + name: test-pf-dev-ta-app0-be-app-0-bucket-a + project: test-pf-dev-ta-app0-be + requester_pays: null + retention_policy: [] + storage_class: STANDARD + terraform_labels: + goog-terraform-provisioned: 'true' + timeouts: null + uniform_bucket_level_access: true + versioning: + - enabled: false + module.project-factory.module.buckets["dev-ta-app0-be/app-0-bucket-b"].google_storage_bucket.bucket[0]: + autoclass: [] + cors: [] + custom_placement_config: [] + default_event_based_hold: null + effective_labels: + goog-terraform-provisioned: 'true' + enable_object_retention: null + encryption: [] + force_destroy: false + hierarchical_namespace: [] + ip_filter: [] + labels: null + lifecycle_rule: [] + location: EUROPE-WEST8 + logging: + - log_bucket: log-bucket + log_object_prefix: log-prefix + name: test-pf-dev-ta-app0-be-app-0-bucket-b + project: test-pf-dev-ta-app0-be + requester_pays: null + retention_policy: [] + storage_class: STANDARD + terraform_labels: + goog-terraform-provisioned: 'true' + timeouts: null + uniform_bucket_level_access: true + versioning: + - enabled: false module.project-factory.module.folder-1-iam["team-a"].google_folder_iam_audit_config.default["storage.googleapis.com"]: audit_log_config: - exempted_members: @@ -872,13 +930,13 @@ counts: google_service_account: 6 google_service_account_iam_binding: 2 google_service_account_iam_member: 2 - google_storage_bucket: 1 + google_storage_bucket: 3 google_storage_bucket_iam_binding: 2 google_storage_project_service_account: 4 google_tags_tag_binding: 2 google_tags_tag_key: 1 google_tags_tag_value: 2 google_tags_tag_value_iam_binding: 1 - modules: 30 - resources: 109 + modules: 32 + resources: 111 terraform_data: 2 diff --git a/tests/modules/project_factory/examples/test-1.yaml b/tests/modules/project_factory/examples/test-1.yaml index 512cddac6..ed4ae9fe1 100644 --- a/tests/modules/project_factory/examples/test-1.yaml +++ b/tests/modules/project_factory/examples/test-1.yaml @@ -13,10 +13,202 @@ # limitations under the License. values: + module.project-factory.module.projects-iam["test-0"].google_project_iam_member.bindings["test_context"]: + condition: + - description: null + expression: resource.matchTag('1234567890/context', 'project-factory') + title: Test context + member: user:user1@example.com + project: foo-test-0 + role: roles/viewer module.project-factory.module.projects["test-0"].google_project.project[0]: + auto_create_network: false + billing_account: 012345-67890A-ABCDEF + deletion_policy: DELETE + effective_labels: + goog-terraform-provisioned: 'true' + owner: foo + folder_id: '1234567890' + labels: + owner: foo + name: foo-test-0 + org_id: null project_id: foo-test-0 + tags: null + terraform_labels: + goog-terraform-provisioned: 'true' + owner: foo + timeouts: null + module.project-factory.module.projects["test-0"].google_project_iam_member.service_agents["compute-system"]: + condition: [] + project: foo-test-0 + role: roles/compute.serviceAgent + module.project-factory.module.projects["test-0"].google_project_iam_member.service_agents["container-engine-robot"]: + condition: [] + project: foo-test-0 + role: roles/container.serviceAgent + module.project-factory.module.projects["test-0"].google_project_iam_member.service_agents["gkenode"]: + condition: [] + project: foo-test-0 + role: roles/container.defaultNodeServiceAgent + module.project-factory.module.projects["test-0"].google_project_service.project_services["compute.googleapis.com"]: + disable_dependent_services: false + disable_on_destroy: false + project: foo-test-0 + service: compute.googleapis.com + timeouts: null + ? module.project-factory.module.projects["test-0"].google_project_service.project_services["contactcenteraiplatform.googleapis.com"] + : disable_dependent_services: false + disable_on_destroy: false + project: foo-test-0 + service: contactcenteraiplatform.googleapis.com + timeouts: null + module.project-factory.module.projects["test-0"].google_project_service.project_services["container.googleapis.com"]: + disable_dependent_services: false + disable_on_destroy: false + project: foo-test-0 + service: container.googleapis.com + timeouts: null + module.project-factory.module.projects["test-0"].google_project_service.project_services["iam.googleapis.com"]: + disable_dependent_services: false + disable_on_destroy: false + project: foo-test-0 + service: iam.googleapis.com + timeouts: null + ? module.project-factory.module.projects["test-0"].google_project_service_identity.default["contactcenteraiplatform.googleapis.com"] + : project: foo-test-0 + service: contactcenteraiplatform.googleapis.com + timeouts: null + module.project-factory.module.projects["test-0"].google_project_service_identity.default["container.googleapis.com"]: + project: foo-test-0 + service: container.googleapis.com + timeouts: null + module.project-factory.module.projects["test-0"].google_tags_tag_key.default["allow-key-creation"]: + allowed_values_regex: null + description: Allow key creation for automation service account + parent: projects/foo-test-0 + purpose: null + purpose_data: null + short_name: allow-key-creation + timeouts: null + module.project-factory.module.projects["test-0"].google_tags_tag_value.default["allow-key-creation/allow"]: + description: Allow key creation + short_name: allow + timeouts: null + ? module.project-factory.module.projects["test-0"].google_tags_tag_value_iam_binding.default["allow-key-creation/allow:roles/resourcemanager.tagUser"] + : condition: [] + members: + - $iam_principals:service_accounts/tags-iam-test/automation/rw + role: roles/resourcemanager.tagUser module.project-factory.module.projects["test-1"].google_project.project[0]: + auto_create_network: false + billing_account: 012345-67890A-ABCDEF + deletion_policy: DELETE + effective_labels: + goog-terraform-provisioned: 'true' + owner: foo + folder_id: '1234567890' + labels: + owner: foo name: Test Project 1 + org_id: null project_id: test-1 + tags: null + terraform_labels: + goog-terraform-provisioned: 'true' + owner: foo + timeouts: null + module.project-factory.module.projects["test-1"].google_project_iam_member.service_agents["compute-system"]: + condition: [] + project: test-1 + role: roles/compute.serviceAgent + module.project-factory.module.projects["test-1"].google_project_service.project_services["compute.googleapis.com"]: + disable_dependent_services: false + disable_on_destroy: false + project: test-1 + service: compute.googleapis.com + timeouts: null + ? module.project-factory.module.projects["test-1"].google_project_service.project_services["contactcenteraiplatform.googleapis.com"] + : disable_dependent_services: false + disable_on_destroy: false + project: test-1 + service: contactcenteraiplatform.googleapis.com + timeouts: null + module.project-factory.module.projects["test-1"].google_project_service.project_services["iam.googleapis.com"]: + disable_dependent_services: false + disable_on_destroy: false + project: test-1 + service: iam.googleapis.com + timeouts: null + ? module.project-factory.module.projects["test-1"].google_project_service_identity.default["contactcenteraiplatform.googleapis.com"] + : project: test-1 + service: contactcenteraiplatform.googleapis.com + timeouts: null + module.project-factory.module.projects["test-1"].google_tags_tag_binding.binding["test"]: + tag_value: $tag_values/ + timeouts: null + module.project-factory.module.projects["test-2"].data.google_storage_project_service_account.gcs_sa[0]: + project: bar-test-2 + user_project: null module.project-factory.module.projects["test-2"].google_project.project[0]: + auto_create_network: false + billing_account: 012345-67890A-ABCDEF + deletion_policy: DELETE + effective_labels: + goog-terraform-provisioned: 'true' + owner: foo + folder_id: '1234567890' + labels: + owner: foo + name: bar-test-2 + org_id: null project_id: bar-test-2 + tags: null + terraform_labels: + goog-terraform-provisioned: 'true' + owner: foo + timeouts: null + module.project-factory.module.projects["test-2"].google_project_iam_member.service_agents["compute-system"]: + condition: [] + project: bar-test-2 + role: roles/compute.serviceAgent + module.project-factory.module.projects["test-2"].google_project_service.project_services["compute.googleapis.com"]: + disable_dependent_services: false + disable_on_destroy: false + project: bar-test-2 + service: compute.googleapis.com + timeouts: null + module.project-factory.module.projects["test-2"].google_project_service.project_services["iam.googleapis.com"]: + disable_dependent_services: false + disable_on_destroy: false + project: bar-test-2 + service: iam.googleapis.com + timeouts: null + module.project-factory.module.projects["test-2"].google_project_service.project_services["storage.googleapis.com"]: + disable_dependent_services: false + disable_on_destroy: false + project: bar-test-2 + service: storage.googleapis.com + timeouts: null + module.project-factory.terraform_data.defaults_preconditions: + input: null + output: null + triggers_replace: null + module.project-factory.terraform_data.project-preconditions: + input: null + output: null + triggers_replace: null + +counts: + google_project: 3 + google_project_iam_member: 6 + google_project_service: 10 + google_project_service_identity: 3 + google_storage_project_service_account: 1 + google_tags_tag_binding: 1 + google_tags_tag_key: 1 + google_tags_tag_value: 1 + google_tags_tag_value_iam_binding: 1 + modules: 5 + resources: 29 + terraform_data: 2