diff --git a/CHANGELOG.md b/CHANGELOG.md index 09ac5177e..e3082a02e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,9 @@ All notable changes to this project will be documented in this file. ### FAST +- [[#3381](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/3381)] Fix typo in fast stage 0 provider template ([ludoo](https://github.com/ludoo)) +- [[#3379](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/3379)] Allow FAST stage 0 provider template to work with universe ([ludoo](https://github.com/ludoo)) +- [[#3378](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/3378)] Allow forcing jit service agents generation for universe in project and project factory modules ([ludoo](https://github.com/ludoo)) - [[#3347](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/3347)] Add support for billing export in 0-org-setup ([kovagoadam](https://github.com/kovagoadam)) - [[#3364](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/3364)] Add missing billing viewer role for org-ro service account in org-set… ([norbert-loderer](https://github.com/norbert-loderer)) - [[#3357](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/3357)] Fixed small typo in project-factory module with log-buckets ([kovagoadam](https://github.com/kovagoadam)) @@ -23,6 +26,13 @@ All notable changes to this project will be documented in this file. ### MODULES +- [[#3380](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/3380)] Lightly refactor service agents locals in project module ([ludoo](https://github.com/ludoo)) +- [[#3378](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/3378)] Allow forcing jit service agents generation for universe in project and project factory modules ([ludoo](https://github.com/ludoo)) +- [[#3376](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/3376)] Allow project id with universe prefix in project module ([ludoo](https://github.com/ludoo)) +- [[#3375](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/3375)] Fix service account module datasource when universe is set ([ludoo](https://github.com/ludoo)) +- [[#3374](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/3374)] Fix admin_approval field access in VPC-SC module ([juliocc](https://github.com/juliocc)) +- [[#3372](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/3372)] feat: add support for SCC Custom Security Health Analytics module in … ([vannicktrinquier](https://github.com/vannicktrinquier)) +- [[#3365](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/3365)] Adding support for managed connection pooling in CloudSQL ([ramja-google](https://github.com/ramja-google)) - [[#3369](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/3369)] Make project id optional in GCS module ([ludoo](https://github.com/ludoo)) - [[#3347](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/3347)] Add support for billing export in 0-org-setup ([kovagoadam](https://github.com/kovagoadam)) - [[#3353](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/3353)] Add custom error response policy for route rules to external application load balancer module. ([patricklubach](https://github.com/patricklubach)) diff --git a/fast/stages/0-org-setup/assets/providers.tf.tpl b/fast/stages/0-org-setup/assets/providers.tf.tpl index 2660477db..2ff52bb27 100644 --- a/fast/stages/0-org-setup/assets/providers.tf.tpl +++ b/fast/stages/0-org-setup/assets/providers.tf.tpl @@ -17,13 +17,26 @@ terraform { backend "gcs" { bucket = "${bucket}" + %{~ if try(universe_domain, null) == null ~} impersonate_service_account = "${service_account}" - %{ if try(prefix, null) != null }prefix = "${prefix}"%{ endif } + %{~ endif ~} + %{~ if try(prefix, null) != null ~} + prefix = "${prefix}" + %{~ endif ~} + %{~ if try(universe_domain, null) != null ~} + storage_custom_endpoint = "https://storage.${universe_domain}/storage/v1/b" + %{~ endif ~} } } provider "google" { impersonate_service_account = "${service_account}" + %{~ if try(universe_domain, null) != null ~} + universe_domain = "${universe_domain}" + %{~ endif ~} } provider "google-beta" { impersonate_service_account = "${service_account}" + %{~ if try(universe_domain, null) != null ~} + universe_domain = "${universe_domain}" + %{~ endif ~} } diff --git a/fast/stages/0-org-setup/output-files.tf b/fast/stages/0-org-setup/output-files.tf index f59abec7e..68f542b1b 100644 --- a/fast/stages/0-org-setup/output-files.tf +++ b/fast/stages/0-org-setup/output-files.tf @@ -51,6 +51,11 @@ locals { id = local.organization_id } prefix = local.defaults.prefix + universe = try( + local.project_defaults.overrides.universe, + local.project_defaults.defaults.universe, + null + ) } org-setup = { automation = { @@ -79,6 +84,11 @@ locals { ) } } + of_universe_domain = try( + local.project_defaults.overrides.universe.domain, + local.project_defaults.defaults.universe.domain, + null + ) } resource "local_file" "providers" { @@ -93,6 +103,7 @@ resource "local_file" "providers" { service_account = lookup( local.of_service_accounts, each.value.service_account, each.value.service_account ) + universe_domain = local.of_universe_domain }) } @@ -108,6 +119,7 @@ resource "google_storage_bucket_object" "providers" { service_account = lookup( local.of_service_accounts, each.value.service_account, each.value.service_account ) + universe_domain = local.of_universe_domain }) } diff --git a/fast/stages/0-org-setup/schemas/defaults.schema.json b/fast/stages/0-org-setup/schemas/defaults.schema.json index 73f699afc..98d662589 100644 --- a/fast/stages/0-org-setup/schemas/defaults.schema.json +++ b/fast/stages/0-org-setup/schemas/defaults.schema.json @@ -314,9 +314,19 @@ "type": "object", "additionalProperties": false, "required": [ + "domain", "prefix" ], "properties": { + "domain": { + "type": "string" + }, + "forced_jit_service_identities": { + "type": "array", + "items": { + "type": "string" + } + }, "prefix": { "type": "string" }, @@ -479,9 +489,19 @@ "type": "object", "additionalProperties": false, "required": [ + "domain", "prefix" ], "properties": { + "domain": { + "type": "string" + }, + "forced_jit_service_identities": { + "type": "array", + "items": { + "type": "string" + } + }, "prefix": { "type": "string" }, @@ -674,4 +694,4 @@ } } } -} \ 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 098ea9d8a..4f06e12e7 100644 --- a/fast/stages/0-org-setup/schemas/project.schema.json +++ b/fast/stages/0-org-setup/schemas/project.schema.json @@ -490,18 +490,24 @@ "additionalProperties": false, "properties": { "prefix": { - "type": "string", - "unavailable_services": { - "type": "array", - "items": { - "type": "string" - } - }, - "unavailable_service_identities": { - "type": "array", - "items": { - "type": "string" - } + "type": "string" + }, + "forced_jit_service_identities": { + "type": "array", + "items": { + "type": "string" + } + }, + "unavailable_services": { + "type": "array", + "items": { + "type": "string" + } + }, + "unavailable_service_identities": { + "type": "array", + "items": { + "type": "string" } } } @@ -535,7 +541,6 @@ "location": { "type": "string" } - } } } @@ -838,4 +843,4 @@ } } } -} \ No newline at end of file +} diff --git a/modules/iam-service-account/main.tf b/modules/iam-service-account/main.tf index cf7eb1b96..1ea6d83ac 100644 --- a/modules/iam-service-account/main.tf +++ b/modules/iam-service-account/main.tf @@ -50,8 +50,12 @@ locals { } data "google_service_account" "service_account" { - count = var.service_account_create ? 0 : 1 - project = local.project_id + count = var.service_account_create ? 0 : 1 + project = ( + strcontains(local.project_id, ":") + ? join(".", reverse(split(":", local.project_id))) + : local.project_id + ) account_id = "${local.prefix}${local.name}" } diff --git a/modules/project-factory/README.md b/modules/project-factory/README.md index d083981bc..8968b9c10 100644 --- a/modules/project-factory/README.md +++ b/modules/project-factory/README.md @@ -693,11 +693,11 @@ service_accounts: | name | description | type | required | default | |---|---|:---:|:---:|:---:| -| [factories_config](variables.tf#L173) | Path to folder with YAML resource description data files. | object({…}) | ✓ | | +| [factories_config](variables.tf#L175) | Path to folder with YAML resource description data files. | object({…}) | ✓ | | | [context](variables.tf#L17) | Context-specific interpolations. | object({…}) | | {} | -| [data_defaults](variables.tf#L36) | Optional default values used when corresponding project or folder data from files are missing. | object({…}) | | {} | -| [data_merges](variables.tf#L108) | 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#L127) | Optional values that override corresponding data from files. Takes precedence over file data and `data_defaults`. | object({…}) | | {} | +| [data_defaults](variables.tf#L36) | Optional default values used when corresponding project or folder data from files are missing. | object({…}) | | {} | +| [data_merges](variables.tf#L109) | 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#L128) | 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({…})) | | {} | diff --git a/modules/project-factory/schemas/project.schema.json b/modules/project-factory/schemas/project.schema.json index 098ea9d8a..ff7264528 100644 --- a/modules/project-factory/schemas/project.schema.json +++ b/modules/project-factory/schemas/project.schema.json @@ -490,18 +490,24 @@ "additionalProperties": false, "properties": { "prefix": { - "type": "string", - "unavailable_services": { - "type": "array", - "items": { - "type": "string" - } - }, - "unavailable_service_identities": { - "type": "array", - "items": { - "type": "string" - } + "type": "string" + }, + "forced_jit_service_identities": { + "type": "array", + "items": { + "type": "string" + } + }, + "unavailable_services": { + "type": "array", + "items": { + "type": "string" + } + }, + "unavailable_service_identities": { + "type": "array", + "items": { + "type": "string" } } } @@ -535,7 +541,6 @@ "location": { "type": "string" } - } } } diff --git a/modules/project-factory/variables.tf b/modules/project-factory/variables.tf index e3462fd8e..bbfa78d76 100644 --- a/modules/project-factory/variables.tf +++ b/modules/project-factory/variables.tf @@ -92,6 +92,7 @@ variable "data_defaults" { tag_bindings = optional(map(string), {}) universe = optional(object({ prefix = string + forced_jit_service_identities = optional(list(string), []) unavailable_service_identities = optional(list(string), []) unavailable_services = optional(list(string), []) })) @@ -157,6 +158,7 @@ variable "data_overrides" { tag_bindings = optional(map(string)) universe = optional(object({ prefix = string + forced_jit_service_identities = optional(list(string), []) unavailable_service_identities = optional(list(string), []) unavailable_services = optional(list(string), []) })) diff --git a/modules/project/README.md b/modules/project/README.md index 3f5927d9c..8bba98c4f 100644 --- a/modules/project/README.md +++ b/modules/project/README.md @@ -1910,8 +1910,8 @@ alerts: | [tag_bindings](variables-tags.tf#L82) | Tag bindings for this project, in key => tag value id format. | map(string) | | null | | [tags](variables-tags.tf#L89) | 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#L154) | Fine-grained control on tag resource and IAM creation. | object({…}) | | {} | -| [universe](variables.tf#L303) | GCP universe where to deploy the project. The prefix will be prepended to the project id. | object({…}) | | null | -| [vpc_sc](variables.tf#L313) | VPC-SC configuration for the project, use when `ignore_changes` for resources is set in the VPC-SC module. | object({…}) | | null | +| [universe](variables.tf#L303) | GCP universe where to deploy the project. The prefix will be prepended to the project id. | object({…}) | | null | +| [vpc_sc](variables.tf#L314) | VPC-SC configuration for the project, use when `ignore_changes` for resources is set in the VPC-SC module. | object({…}) | | null | ## Outputs diff --git a/modules/project/cmek.tf b/modules/project/cmek.tf index ec1482df3..ef71119a8 100644 --- a/modules/project/cmek.tf +++ b/modules/project/cmek.tf @@ -51,11 +51,15 @@ locals { } _all_cmek_bindings = flatten([ for service, keys in var.service_encryption_key_ids : [ - for dep in try(local._cmek_agents_by_service[service], [for x in local._service_agents_by_api[service] : x.name], [service]) : [ + for dep in try( + local._cmek_agents_by_service[service], + [for x in local.service_agents_by_api[service] : x.name], + [service] + ) : [ for key in keys : { key_id = key - agent_name = local._aliased_service_agents[dep].name - agent_email = local._aliased_service_agents[dep].iam_email + agent_name = local.aliased_service_agents[dep].name + agent_email = local.aliased_service_agents[dep].iam_email } ] ] diff --git a/modules/project/main.tf b/modules/project/main.tf index 42845fd44..3979a14d7 100644 --- a/modules/project/main.tf +++ b/modules/project/main.tf @@ -68,9 +68,17 @@ locals { } ) ) - project_id = "${local.universe_prefix}${local.prefix}${var.name}" - universe_prefix = var.universe == null ? "" : "${var.universe.prefix}:" - available_services = tolist(setsubtract(var.services, try(var.universe.unavailable_services, []))) + project_id = ( + strcontains("${local.prefix}${var.name}", ":") + ? "${local.prefix}${var.name}" + : "${local.universe_prefix}${local.prefix}${var.name}" + ) + universe_prefix = var.universe == null ? "" : "${var.universe.prefix}:" + # available services are those declared, minus any unsupported by universe + available_services = tolist(setsubtract( + var.services, + try(var.universe.unavailable_services, []) + )) } data "google_project" "project" { diff --git a/modules/project/service-agents.tf b/modules/project/service-agents.tf index 43a9f4b15..2b0d1bfe7 100644 --- a/modules/project/service-agents.tf +++ b/modules/project/service-agents.tf @@ -17,103 +17,116 @@ # tfdoc:file:description Service agents supporting resources. locals { - services = [ - for s in distinct(concat( - local.available_services, try(var.project_reuse.attributes.services_enabled, []) - )) : s if !contains(local._universe_unavailable_si, s) - ] - _service_agents_data = yamldecode(file("${path.module}/service-agents.yaml")) - # map of api => list of agents - _service_agents_by_api = { - for agent in local._service_agents_data : - coalesce(agent.api, "cloudservices") => agent... # cloudservices api is null - } - _universe_domain = ( - var.universe == null - ? "" - : "${var.universe.prefix}-system." - ) - _universe_unavailable_si = try(var.universe.unavailable_service_identities, []) - # map of service agent name => agent details for this project - _project_service_agents_0 = merge([ + _sa_raw = yamldecode(file("${path.module}/service-agents.yaml")) + # initial map of service agents by name, defining agent email + _sa_0 = merge([ for api in concat(local.services, ["cloudservices"]) : { - for agent in lookup(local._service_agents_by_api, api, []) : + for agent in lookup(local.service_agents_by_api, api, []) : (agent.name) => merge(agent, { email = ( - # If universe variable is set, enfore the use of the service-PROJECT_NUMBER@gcp-sa-ekms.UNIVERSE-system.iam.gserviceaccount.com - # instead of service-PROJECT_NUMBER@gcp-sa-kms.UNIVERSE-system.iam.gserviceaccount.com - # as in the TPC universes, the partner KMS is enforced by design - var.universe != null && api == "cloudkms.googleapis.com" - ? format("service-%s@gcp-sa-ekms.%siam.gserviceaccount.com", local.project.number, local._universe_domain) + api == "cloudservices" + ? format( + "%s@cloudservices.%siam.gserviceaccount.com", + local.project.number, + local._u_domain + ) : ( - var.universe == null || api != "cloudservices" - ? templatestring(agent.identity, { project_number = local.project.number, universe_domain = local._universe_domain }) - : format("%s@cloudservices.%siam.gserviceaccount.com", local.project.number, local._universe_domain) + var.universe == null || !startswith(api, "cloudkms.") + ? templatestring(agent.identity, { + project_number = local.project.number + universe_domain = local._u_domain + }) + # universe uses partner KMS + : format( + "service-%s@gcp-sa-ekms.%siam.gserviceaccount.com", + local.project.number, + local._u_domain + ) ) ) }) } ]...) - _project_service_agents = { - for k, v in local._project_service_agents_0 : - k => merge(v, { - iam_email = "serviceAccount:${v.email}" - create_jit = v.api == null ? false : contains(local.available_services, v.api) - }) if !contains(local._universe_unavailable_si, k) + # final map of service agents by name including JIT creation flag + _sa = { + for k, v in local._sa_0 : k => merge(v, { + iam_email = "serviceAccount:${v.email}" + create_jit = ( + v.api == null ? false : contains(local.available_services, v.api) + ) + # skip identities which are unavailable in the defined universe + }) if !contains(local._u_unavailable_si, k) } - # list of APIs with primary agents that should be created for the - # current project, if the user requested it - primary_service_agents = [ - for agent in local._project_service_agents : agent.api if( - agent.is_primary && - var.service_agents_config.create_primary_agents && - agent.create_jit - ) - ] - # list of roles that should be granted to service agents for the - # current project, if the user requested it - service_agent_roles = { - for agent in local._project_service_agents : - (agent.name) => { - role = agent.role - iam_email = agent.iam_email - } - if alltrue([ - var.service_agents_config.grant_default_roles, - agent.role != null, - # FIXME: granting roles to the non-primary agents listed below - # currently fails, possibly because the agents doesn't exist - # after API activation. As a workaround, automatic role - # assignment for these agents is disabled. - !contains([ - "apigateway", "apigateway-mgmt", "bigqueryspark", "bigquerytardis", - "firebase", "krmapihosting", "krmapihosting-dataplane", "logging", - "networkactions", "prod-bigqueryomni", "scc-notification", "securitycenter", - ], agent.name) - ]) - } - # map of name->agent including all known aliases - _aliased_service_agents = merge( - local._project_service_agents, - flatten([ - for agent_name, agent in local._project_service_agents : [ - for alias in agent.aliases : - { (alias) = agent } - ] - ])... + # map of name => agent for all known aliases + _sa_aliases = merge(local._sa, flatten([ + for agent_name, agent in local._sa : [ + for alias in agent.aliases : { (alias) = agent } + ] + ])...) + _u_domain = ( + var.universe == null ? "" : "${var.universe.prefix}-system." ) - # same as _aliased_service_agents with unneeded fields removed + _u_unavailable_si = try( + var.universe.unavailable_service_identities, [] + ) + # aliased service agents, with unnecessary fields removed aliased_service_agents = { - for k, v in local._aliased_service_agents : - k => { + for k, v in local._sa_aliases : k => { api = v.api display_name = v.display_name email = v.email iam_email = v.iam_email is_primary = v.is_primary + name = v.name role = v.role } } + # service agents to create in this project + primary_service_agents = [ + for agent in local._sa : agent.api if( + # only create if user asked us to (which is the default) + var.service_agents_config.create_primary_agents && ( + # only create if agent is primary and JIT flag is true + (agent.is_primary && agent.create_jit) + || + # or if universe configuration is forcing this agent to be created + contains( + try(var.universe.forced_jit_service_identities, []), + coalesce(agent.api, "-") + ) + ) + ) + ] + # group service agents by api, cloudservices api is null + service_agents_by_api = { + for v in local._sa_raw : coalesce(v.api, "cloudservices") => v... + } + # IAM roles for service agents to create in this project + service_agent_roles = { + for agent in local._sa : (agent.name) => { + role = agent.role + iam_email = agent.iam_email + } if alltrue([ + var.service_agents_config.grant_default_roles, + agent.role != null, + # TODO: improve the detection below + # this skips IAM role grants to the non-primary agents listed below + # as it's failing, possibly because the agents don't exist + # after API activation + !contains([ + "apigateway", "apigateway-mgmt", "bigqueryspark", "bigquerytardis", + "firebase", "krmapihosting", "krmapihosting-dataplane", "logging", + "networkactions", "prod-bigqueryomni", "scc-notification", + "securitycenter" + ], agent.name) + ]) + } + services = [ + for s in distinct(concat( + local.available_services, + try(var.project_reuse.attributes.services_enabled, []) + )) : s if !contains(local._u_unavailable_si, s) + ] } data "google_storage_project_service_account" "gcs_sa" { @@ -146,7 +159,6 @@ resource "google_project_service_identity" "default" { depends_on = [google_project_service.project_services] } - moved { from = google_project_iam_member.servicenetworking[0] to = google_project_iam_member.service_agents["service-networking"] diff --git a/modules/project/variables.tf b/modules/project/variables.tf index 0d14a1f76..828c14807 100644 --- a/modules/project/variables.tf +++ b/modules/project/variables.tf @@ -304,6 +304,7 @@ variable "universe" { description = "GCP universe where to deploy the project. The prefix will be prepended to the project id." type = object({ prefix = string + forced_jit_service_identities = optional(list(string), []) unavailable_services = optional(list(string), []) unavailable_service_identities = optional(list(string), []) }) diff --git a/modules/vpc-sc/access-levels.tf b/modules/vpc-sc/access-levels.tf index 6bdabe284..fd0fd417a 100644 --- a/modules/vpc-sc/access-levels.tf +++ b/modules/vpc-sc/access-levels.tf @@ -57,7 +57,7 @@ resource "google_access_context_manager_access_level" "basic" { allowed_encryption_statuses = ( dp.value.allowed_encryption_statuses ) - require_admin_approval = dp.value.key.require_admin_approval + require_admin_approval = dp.value.require_admin_approval require_corp_owned = dp.value.require_corp_owned require_screen_lock = dp.value.require_screen_lock diff --git a/tests/fast/stages/s0_org_setup/not-simple.yaml b/tests/fast/stages/s0_org_setup/not-simple.yaml index fb1b2efbf..86c44055b 100644 --- a/tests/fast/stages/s0_org_setup/not-simple.yaml +++ b/tests/fast/stages/s0_org_setup/not-simple.yaml @@ -57,8 +57,8 @@ values: \ the specific language governing permissions and\n * limitations under the\ \ License.\n */\n\nterraform {\n backend \"gcs\" {\n bucket \ \ = \"ft0-prod-iac-core-0-iac-org-state\"\n impersonate_service_account\ - \ = \"iac-org-rw@ft0-prod-iac-core-0.iam.gserviceaccount.com\"\n \n }\n\ - }\nprovider \"google\" {\n impersonate_service_account = \"iac-org-rw@ft0-prod-iac-core-0.iam.gserviceaccount.com\"\ + \ = \"iac-org-rw@ft0-prod-iac-core-0.iam.gserviceaccount.com\"\n }\n}\nprovider\ + \ \"google\" {\n impersonate_service_account = \"iac-org-rw@ft0-prod-iac-core-0.iam.gserviceaccount.com\"\ \n}\nprovider \"google-beta\" {\n impersonate_service_account = \"iac-org-rw@ft0-prod-iac-core-0.iam.gserviceaccount.com\"\ \n}\n" content_disposition: null @@ -89,8 +89,8 @@ values: \ the specific language governing permissions and\n * limitations under the\ \ License.\n */\n\nterraform {\n backend \"gcs\" {\n bucket \ \ = \"ft0-prod-iac-core-0-iac-org-state\"\n impersonate_service_account\ - \ = \"iac-org-rw@ft0-prod-iac-core-0.iam.gserviceaccount.com\"\n \n }\n\ - }\nprovider \"google\" {\n impersonate_service_account = \"iac-org-rw@ft0-prod-iac-core-0.iam.gserviceaccount.com\"\ + \ = \"iac-org-rw@ft0-prod-iac-core-0.iam.gserviceaccount.com\"\n }\n}\nprovider\ + \ \"google\" {\n impersonate_service_account = \"iac-org-rw@ft0-prod-iac-core-0.iam.gserviceaccount.com\"\ \n}\nprovider \"google-beta\" {\n impersonate_service_account = \"iac-org-rw@ft0-prod-iac-core-0.iam.gserviceaccount.com\"\ \n}\n" content_disposition: null @@ -243,7 +243,7 @@ values: google_storage_bucket_object.tfvars["globals"]: bucket: ft0-prod-iac-core-0-iac-outputs cache_control: null - content: '{"billing_account":{"id":"012345-012345-012345"},"groups":{"domain":"domain:example.org","gcp-billing-admins":"group:gcp-billing-admins@example.org","gcp-devops":"group:gcp-devops@example.org","gcp-network-admins":"group:gcp-network-admins@example.org","gcp-organization-admins":"group:fabric-fast-owners@google.com","gcp-secops-admins":"group:gcp-secops-admins@example.org","gcp-security-admins":"group:gcp-security-admins@example.org","gcp-support":"group:gcp-support@example.org"},"locations":{"bigquery":"europe-west1","logging":"europe-west1","pubsub":[],"storage":"eu"},"organization":{"customer_id":"abcd123456","domain":"example.org","id":"1234567890"},"prefix":"ft0"}' + content: '{"billing_account":{"id":"012345-012345-012345"},"groups":{"domain":"domain:example.org","gcp-billing-admins":"group:gcp-billing-admins@example.org","gcp-devops":"group:gcp-devops@example.org","gcp-network-admins":"group:gcp-network-admins@example.org","gcp-organization-admins":"group:fabric-fast-owners@google.com","gcp-secops-admins":"group:gcp-secops-admins@example.org","gcp-security-admins":"group:gcp-security-admins@example.org","gcp-support":"group:gcp-support@example.org"},"locations":{"bigquery":"europe-west1","logging":"europe-west1","pubsub":[],"storage":"eu"},"organization":{"customer_id":"abcd123456","domain":"example.org","id":"1234567890"},"prefix":"ft0","universe":null}' content_disposition: null content_encoding: null content_language: null @@ -306,8 +306,8 @@ values: \ the specific language governing permissions and\n * limitations under the\ \ License.\n */\n\nterraform {\n backend \"gcs\" {\n bucket \ \ = \"ft0-prod-iac-core-0-iac-org-state\"\n impersonate_service_account\ - \ = \"iac-org-rw@ft0-prod-iac-core-0.iam.gserviceaccount.com\"\n \n }\n\ - }\nprovider \"google\" {\n impersonate_service_account = \"iac-org-rw@ft0-prod-iac-core-0.iam.gserviceaccount.com\"\ + \ = \"iac-org-rw@ft0-prod-iac-core-0.iam.gserviceaccount.com\"\n }\n}\nprovider\ + \ \"google\" {\n impersonate_service_account = \"iac-org-rw@ft0-prod-iac-core-0.iam.gserviceaccount.com\"\ \n}\nprovider \"google-beta\" {\n impersonate_service_account = \"iac-org-rw@ft0-prod-iac-core-0.iam.gserviceaccount.com\"\ \n}\n" content_base64: null @@ -327,8 +327,8 @@ values: \ the specific language governing permissions and\n * limitations under the\ \ License.\n */\n\nterraform {\n backend \"gcs\" {\n bucket \ \ = \"ft0-prod-iac-core-0-iac-org-state\"\n impersonate_service_account\ - \ = \"iac-org-rw@ft0-prod-iac-core-0.iam.gserviceaccount.com\"\n \n }\n\ - }\nprovider \"google\" {\n impersonate_service_account = \"iac-org-rw@ft0-prod-iac-core-0.iam.gserviceaccount.com\"\ + \ = \"iac-org-rw@ft0-prod-iac-core-0.iam.gserviceaccount.com\"\n }\n}\nprovider\ + \ \"google\" {\n impersonate_service_account = \"iac-org-rw@ft0-prod-iac-core-0.iam.gserviceaccount.com\"\ \n}\nprovider \"google-beta\" {\n impersonate_service_account = \"iac-org-rw@ft0-prod-iac-core-0.iam.gserviceaccount.com\"\ \n}\n" content_base64: null @@ -426,7 +426,7 @@ values: sensitive_content: null source: null local_file.tfvars["globals"]: - content: '{"billing_account":{"id":"012345-012345-012345"},"groups":{"domain":"domain:example.org","gcp-billing-admins":"group:gcp-billing-admins@example.org","gcp-devops":"group:gcp-devops@example.org","gcp-network-admins":"group:gcp-network-admins@example.org","gcp-organization-admins":"group:fabric-fast-owners@google.com","gcp-secops-admins":"group:gcp-secops-admins@example.org","gcp-security-admins":"group:gcp-security-admins@example.org","gcp-support":"group:gcp-support@example.org"},"locations":{"bigquery":"europe-west1","logging":"europe-west1","pubsub":[],"storage":"eu"},"organization":{"customer_id":"abcd123456","domain":"example.org","id":"1234567890"},"prefix":"ft0"}' + content: '{"billing_account":{"id":"012345-012345-012345"},"groups":{"domain":"domain:example.org","gcp-billing-admins":"group:gcp-billing-admins@example.org","gcp-devops":"group:gcp-devops@example.org","gcp-network-admins":"group:gcp-network-admins@example.org","gcp-organization-admins":"group:fabric-fast-owners@google.com","gcp-secops-admins":"group:gcp-secops-admins@example.org","gcp-security-admins":"group:gcp-security-admins@example.org","gcp-support":"group:gcp-support@example.org"},"locations":{"bigquery":"europe-west1","logging":"europe-west1","pubsub":[],"storage":"eu"},"organization":{"customer_id":"abcd123456","domain":"example.org","id":"1234567890"},"prefix":"ft0","universe":null}' content_base64: null directory_permission: '0777' file_permission: '0644' @@ -472,6 +472,11 @@ values: condition: [] member: serviceAccount:iac-security-rw@ft0-prod-iac-core-0.iam.gserviceaccount.com role: roles/billing.user + module.billing-accounts["default"].google_billing_account_iam_member.bindings["billing_viewer_org_ro"]: + billing_account_id: 012345-012345-012345 + condition: [] + member: serviceAccount:iac-org-ro@ft0-prod-iac-core-0.iam.gserviceaccount.com + role: roles/billing.viewer module.factory.module.bigquery-datasets["billing-0/billing_export"].google_bigquery_dataset.default: dataset_id: billing_export default_encryption_configuration: [] @@ -492,11 +497,6 @@ values: terraform_labels: goog-terraform-provisioned: 'true' timeouts: null - module.billing-accounts["default"].google_billing_account_iam_member.bindings["billing_viewer_org_ro"]: - billing_account_id: 012345-012345-012345 - condition: [] - member: serviceAccount:iac-org-ro@ft0-prod-iac-core-0.iam.gserviceaccount.com - role: roles/billing.viewer module.factory.module.buckets["iac-0/iac-org-state"].google_storage_bucket.bucket[0]: autoclass: [] cors: []