From b069b6790979bf60e593b70debc117ccd6fd3f1a Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Mon, 27 Apr 2026 09:01:03 +0200 Subject: [PATCH] Fix regressions in FAST v55.2.0 (#3910) * fix org-setup outputs * start work on trickling down tag_vars through stages * fixes * tflint * fix vpn context in fast networking stage * automated review fixes * review comments --- fast/stages/0-org-setup/output-files.tf | 18 +++++++++++- fast/stages/2-networking/README.md | 9 +++--- fast/stages/2-networking/factory-vpns.tf | 2 +- fast/stages/2-networking/main.tf | 11 ++++---- fast/stages/2-networking/variables-fast.tf | 20 +++++++++---- fast/stages/2-networking/variables.tf | 26 +++++++++-------- fast/stages/2-project-factory/README.md | 6 ++-- fast/stages/2-project-factory/main.tf | 14 ++++++---- .../2-project-factory/variables-fast.tf | 14 +++++----- fast/stages/2-project-factory/variables.tf | 8 ++++-- fast/stages/2-security/README.md | 9 +++--- fast/stages/2-security/main.tf | 11 ++++---- fast/stages/2-security/variables-fast.tf | 20 +++++++++---- fast/stages/2-security/variables.tf | 24 +++++++++------- modules/project-factory/README.md | 28 +++++++++---------- modules/project-factory/outputs.tf | 11 ++++++++ 16 files changed, 146 insertions(+), 85 deletions(-) diff --git a/fast/stages/0-org-setup/output-files.tf b/fast/stages/0-org-setup/output-files.tf index 27133174d..6aaaab8ee 100644 --- a/fast/stages/0-org-setup/output-files.tf +++ b/fast/stages/0-org-setup/output-files.tf @@ -40,11 +40,26 @@ locals { module.factory.project_ids ) storage_buckets = module.factory.storage_buckets - tag_keys = module.organization[0].tag_keys + tag_keys = merge( + local.ctx.tag_keys, + local.org_tag_keys + ) tag_values = merge( local.ctx.tag_values, local.org_tag_values ) + tag_vars = { + projects = merge([ + for k, v in module.factory.projects : { + (k) = { for kk, vv in v.tag_vars : kk => vv } + } if length(v.tag_vars) > 0 + ]...) + organization = { + for k, v in module.organization[0].tag_keys : + # the provider returns allowed_values_regex set to "" not null + k => v.namespaced_name if try(v.allowed_values_regex, "") != "" + } + } }) of_logging_sinks = { # Include project_id in the destination if supported (omitted for @@ -128,6 +143,7 @@ locals { } tag_keys = local.of_ctx.tag_keys tag_values = local.of_ctx.tag_values + tag_vars = local.of_ctx.tag_vars vpc_self_links = { for k, v in module.vpcs.vpcs : k => v.id } diff --git a/fast/stages/2-networking/README.md b/fast/stages/2-networking/README.md index 5d8e6bd24..4b2cd4e91 100644 --- a/fast/stages/2-networking/README.md +++ b/fast/stages/2-networking/README.md @@ -384,7 +384,7 @@ Internally created resources are mapped to context namespaces, and use specific | [prefix](variables-fast.tf#L75) | Prefix used for resources that need unique names. Use a maximum of 9 chars for organizations, and 11 chars for tenants. | string | ✓ | | | [context](variables.tf#L17) | Context-specific interpolations. | object({…}) | | {} | | [custom_roles](variables-fast.tf#L25) | Custom roles defined at the org level, in key => id format. | map(string) | | {} | -| [factories_config](variables.tf#L37) | Configuration for the resource factories or external data. | object({…}) | | {} | +| [factories_config](variables.tf#L41) | Configuration for the resource factories or external data. | object({…}) | | {} | | [folder_ids](variables-fast.tf#L33) | Folders created in the bootstrap stage. | map(string) | | {} | | [iam_principals](variables-fast.tf#L41) | IAM-format principals. | map(string) | | {} | | [kms_keys](variables-fast.tf#L50) | KMS key ids. | map(string) | | {} | @@ -392,9 +392,10 @@ Internally created resources are mapped to context namespaces, and use specific | [project_ids](variables-fast.tf#L85) | Projects created in the bootstrap stage. | map(string) | | {} | | [service_accounts](variables-fast.tf#L93) | Service accounts created in the bootstrap stage. | map(string) | | {} | | [storage_buckets](variables-fast.tf#L101) | Storage buckets created in the bootstrap stage. | map(string) | | {} | -| [tag_keys](variables-fast.tf#L109) | FAST-managed resource manager tag keys. | map(object({…})) | | {} | -| [tag_values](variables-fast.tf#L120) | FAST-managed resource manager tag values. | map(string) | | {} | -| [universe](variables-fast.tf#L128) | GCP universe where to deploy projects. The prefix will be prepended to the project id. | object({…}) | | null | +| [tag_keys](variables-fast.tf#L109) | FAST-managed resource manager tag keys. | map(string) | | {} | +| [tag_values](variables-fast.tf#L117) | FAST-managed resource manager tag values. | map(string) | | {} | +| [tag_vars](variables-fast.tf#L125) | FAST-managed resource manager tag key namespaced names. | object({…}) | | {} | +| [universe](variables-fast.tf#L136) | GCP universe where to deploy projects. The prefix will be prepended to the project id. | object({…}) | | null | ## Outputs diff --git a/fast/stages/2-networking/factory-vpns.tf b/fast/stages/2-networking/factory-vpns.tf index ae5e1908c..b2c0a556c 100644 --- a/fast/stages/2-networking/factory-vpns.tf +++ b/fast/stages/2-networking/factory-vpns.tf @@ -107,7 +107,7 @@ module "vpn-ha" { } context = { locations = local.ctx.locations - network = local.ctx_vpcs.names + networks = local.ctx_vpcs.names project_ids = local.ctx_projects.project_ids routers = local.ctx_routers.names vpn_gateways = local.ctx_gateways diff --git a/fast/stages/2-networking/main.tf b/fast/stages/2-networking/main.tf index 507edcae9..8e8b785d0 100644 --- a/fast/stages/2-networking/main.tf +++ b/fast/stages/2-networking/main.tf @@ -48,11 +48,12 @@ locals { tag_keys = merge(var.tag_keys, local._ctx.tag_keys) tag_values = merge(var.tag_values, local._ctx.tag_values) tag_vars = { - projects = try(local._ctx.tag_vars.projects, {}) - organization = merge({ - for k, v in var.tag_keys : k => v.namespaced_name - if v.allowed_values_regex != null - }, try(local._ctx.tag_vars.organization, {})) + organization = merge( + var.tag_vars.organization, local._ctx.tag_vars.organization + ) + projects = merge( + var.tag_vars.projects, local._ctx.tag_vars.projects + ) } vpc_sc_perimeters = merge(var.perimeters, local._ctx.vpc_sc_perimeters) }) diff --git a/fast/stages/2-networking/variables-fast.tf b/fast/stages/2-networking/variables-fast.tf index 6a65b0cc6..6d77e4bd0 100644 --- a/fast/stages/2-networking/variables-fast.tf +++ b/fast/stages/2-networking/variables-fast.tf @@ -109,12 +109,9 @@ variable "storage_buckets" { variable "tag_keys" { # tfdoc:variable:source 0-org-setup description = "FAST-managed resource manager tag keys." - type = map(object({ - namespaced_name = string - allowed_values_regex = optional(string) - })) - default = {} - nullable = false + type = map(string) + nullable = false + default = {} } variable "tag_values" { @@ -125,6 +122,17 @@ variable "tag_values" { default = {} } +variable "tag_vars" { + # tfdoc:variable:source 0-org-setup + description = "FAST-managed resource manager tag key namespaced names." + type = object({ + projects = optional(map(map(string)), {}) + organization = optional(map(string), {}) + }) + nullable = false + default = {} +} + variable "universe" { # tfdoc:variable:source 0-org-setup description = "GCP universe where to deploy projects. The prefix will be prepended to the project id." diff --git a/fast/stages/2-networking/variables.tf b/fast/stages/2-networking/variables.tf index 289fdd1f5..173e2f94a 100644 --- a/fast/stages/2-networking/variables.tf +++ b/fast/stages/2-networking/variables.tf @@ -17,17 +17,21 @@ variable "context" { description = "Context-specific interpolations." type = object({ - cidr_ranges_sets = optional(map(list(string)), {}) - custom_roles = optional(map(string), {}) - email_addresses = optional(map(string), {}) - folder_ids = optional(map(string), {}) - kms_keys = optional(map(string), {}) - iam_principals = optional(map(string), {}) - locations = optional(map(string), {}) - project_ids = optional(map(string), {}) - storage_buckets = optional(map(string), {}) - tag_keys = optional(map(string), {}) - tag_values = optional(map(string), {}) + cidr_ranges_sets = optional(map(list(string)), {}) + custom_roles = optional(map(string), {}) + email_addresses = optional(map(string), {}) + folder_ids = optional(map(string), {}) + kms_keys = optional(map(string), {}) + iam_principals = optional(map(string), {}) + locations = optional(map(string), {}) + project_ids = optional(map(string), {}) + storage_buckets = optional(map(string), {}) + tag_keys = optional(map(string), {}) + tag_values = optional(map(string), {}) + tag_vars = optional(object({ + projects = optional(map(map(string)), {}) + organization = optional(map(string), {}) + }), {}) vpc_sc_perimeters = optional(map(string), {}) }) default = {} diff --git a/fast/stages/2-project-factory/README.md b/fast/stages/2-project-factory/README.md index c61887756..a4ce8909a 100644 --- a/fast/stages/2-project-factory/README.md +++ b/fast/stages/2-project-factory/README.md @@ -487,7 +487,7 @@ Pattern-based files make specific assumptions: | [data_defaults](variables-projects.tf#L17) | Optional default values used when corresponding project or folder data from files are missing. | object({…}) | | {} | | | [data_merges](variables-projects.tf#L93) | Optional values that will be merged with corresponding data from files. Combines with `data_defaults`, file data, and `data_overrides`. | object({…}) | | {} | | | [data_overrides](variables-projects.tf#L112) | Optional values that override corresponding data from files. Takes precedence over file data and `data_defaults`. | object({…}) | | {} | | -| [factories_config](variables.tf#L37) | Path to folder with YAML resource description data files. | object({…}) | | {} | | +| [factories_config](variables.tf#L41) | Path to folder with YAML resource description data files. | object({…}) | | {} | | | [folder_ids](variables-fast.tf#L42) | Folders created in the bootstrap stage. | map(string) | | {} | 0-org-setup | | [host_project_ids](variables-fast.tf#L58) | Host project for the shared VPC. | map(string) | | {} | 2-networking | | [iam_principals](variables-fast.tf#L50) | IAM-format principals. | map(string) | | {} | 0-org-setup | @@ -495,10 +495,10 @@ Pattern-based files make specific assumptions: | [perimeters](variables-fast.tf#L84) | Optional VPC-SC perimeter ids. | map(string) | | {} | 1-vpcsc | | [project_ids](variables-fast.tf#L102) | Projects created in the bootstrap stage. | map(string) | | {} | 0-org-setup | | [service_accounts](variables-fast.tf#L110) | Service accounts created in the bootstrap stage. | map(string) | | {} | 0-org-setup | -| [stage_name](variables.tf#L58) | FAST stage name. Used to separate output files across different factories. | string | | "2-project-factory" | | +| [stage_name](variables.tf#L62) | FAST stage name. Used to separate output files across different factories. | string | | "2-project-factory" | | | [subnet_self_links](variables-fast.tf#L118) | Shared VPC subnet IDs. | map(map(string)) | | {} | 2-networking | -| [tag_keys](variables-fast.tf#L134) | FAST-managed resource manager tag keys. | map(object({…})) | | {} | 0-org-setup | | [tag_values](variables-fast.tf#L126) | FAST-managed resource manager tag values. | map(string) | | {} | 0-org-setup | +| [tag_vars](variables-fast.tf#L134) | FAST-managed resource manager tag key namespaced names. | object({…}) | | {} | 0-org-setup | | [universe](variables-fast.tf#L145) | GCP universe where to deploy projects. The prefix will be prepended to the project id. | object({…}) | | null | 0-globals | ## Outputs diff --git a/fast/stages/2-project-factory/main.tf b/fast/stages/2-project-factory/main.tf index 070b0087e..338e16262 100644 --- a/fast/stages/2-project-factory/main.tf +++ b/fast/stages/2-project-factory/main.tf @@ -18,7 +18,8 @@ locals { _context = { - for k, v in var.context : k => merge(v, try(local.defaults.context[k], {})) + for k, v in var.context : + k => merge(v, try(local.defaults.context[k], {})) } context = merge(local._context, { vpc_sc_perimeters = merge(var.perimeters, local._context.vpc_sc_perimeters) @@ -104,11 +105,12 @@ module "factory" { ) tag_values = merge(var.tag_values, local.context.tag_values) tag_vars = { - projects = try(local.context.tag_vars.projects, {}) - organization = merge({ - for k, v in var.tag_keys : k => v.namespaced_name - if v.allowed_values_regex != null - }, try(local.context.tag_vars.organization, {})) + organization = merge( + var.tag_vars.organization, local.context.tag_vars.organization + ) + projects = merge( + var.tag_vars.projects, local.context.tag_vars.projects + ) } vpc_sc_perimeters = merge(var.perimeters, local.context.vpc_sc_perimeters) } diff --git a/fast/stages/2-project-factory/variables-fast.tf b/fast/stages/2-project-factory/variables-fast.tf index 5ffe7e595..cdd524411 100644 --- a/fast/stages/2-project-factory/variables-fast.tf +++ b/fast/stages/2-project-factory/variables-fast.tf @@ -131,15 +131,15 @@ variable "tag_values" { default = {} } -variable "tag_keys" { +variable "tag_vars" { # tfdoc:variable:source 0-org-setup - description = "FAST-managed resource manager tag keys." - type = map(object({ - namespaced_name = string - allowed_values_regex = optional(string) - })) - default = {} + description = "FAST-managed resource manager tag key namespaced names." + type = object({ + projects = optional(map(map(string)), {}) + organization = optional(map(string), {}) + }) nullable = false + default = {} } variable "universe" { diff --git a/fast/stages/2-project-factory/variables.tf b/fast/stages/2-project-factory/variables.tf index 7edac58b9..e637055d3 100644 --- a/fast/stages/2-project-factory/variables.tf +++ b/fast/stages/2-project-factory/variables.tf @@ -27,8 +27,12 @@ variable "context" { notification_channels = optional(map(string), {}) project_ids = optional(map(string), {}) tag_values = optional(map(string), {}) - vpc_host_projects = optional(map(string), {}) - vpc_sc_perimeters = optional(map(string), {}) + tag_vars = optional(object({ + projects = optional(map(map(string)), {}) + organization = optional(map(string), {}) + }), {}) + vpc_host_projects = optional(map(string), {}) + vpc_sc_perimeters = optional(map(string), {}) }) default = {} nullable = false diff --git a/fast/stages/2-security/README.md b/fast/stages/2-security/README.md index 402674b25..4d79f282b 100644 --- a/fast/stages/2-security/README.md +++ b/fast/stages/2-security/README.md @@ -191,16 +191,17 @@ A reference Certificate Authority Services (CAS) is also part of this stage, all | [prefix](variables-fast.tf#L57) | 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 | | [context](variables.tf#L17) | Context-specific interpolations. | object({…}) | | {} | | | [custom_roles](variables-fast.tf#L25) | Custom roles defined at the org level, in key => id format. | map(string) | | {} | 0-org-setup | -| [factories_config](variables.tf#L36) | Configuration for the resource factories or external data. | object({…}) | | {} | | +| [factories_config](variables.tf#L40) | Configuration for the resource factories or external data. | object({…}) | | {} | | | [folder_ids](variables-fast.tf#L33) | Folders created in the bootstrap stage. | map(string) | | {} | 0-org-setup | | [iam_principals](variables-fast.tf#L41) | IAM-format principals. | map(string) | | {} | 0-org-setup | | [perimeters](variables-fast.tf#L49) | Optional VPC-SC perimeter ids. | map(string) | | {} | 1-vpcsc | | [project_ids](variables-fast.tf#L67) | Projects created in the bootstrap stage. | map(string) | | {} | 0-org-setup | | [service_accounts](variables-fast.tf#L75) | Service accounts created in the bootstrap stage. | map(string) | | {} | 0-org-setup | | [storage_buckets](variables-fast.tf#L83) | Storage buckets created in the bootstrap stage. | map(string) | | {} | 0-org-setup | -| [tag_keys](variables-fast.tf#L91) | FAST-managed resource manager tag keys. | map(object({…})) | | {} | 0-org-setup | -| [tag_values](variables-fast.tf#L102) | FAST-managed resource manager tag values. | map(string) | | {} | 0-org-setup | -| [universe](variables-fast.tf#L110) | GCP universe where to deploy projects. The prefix will be prepended to the project id. | object({…}) | | null | 0-org-setup | +| [tag_keys](variables-fast.tf#L91) | FAST-managed resource manager tag keys. | map(string) | | {} | 0-org-setup | +| [tag_values](variables-fast.tf#L99) | FAST-managed resource manager tag values. | map(string) | | {} | 0-org-setup | +| [tag_vars](variables-fast.tf#L107) | FAST-managed resource manager tag key namespaced names. | object({…}) | | {} | 0-org-setup | +| [universe](variables-fast.tf#L118) | GCP universe where to deploy projects. The prefix will be prepended to the project id. | object({…}) | | null | 0-org-setup | ## Outputs diff --git a/fast/stages/2-security/main.tf b/fast/stages/2-security/main.tf index 74997766d..41049b701 100644 --- a/fast/stages/2-security/main.tf +++ b/fast/stages/2-security/main.tf @@ -43,11 +43,12 @@ locals { tag_keys = merge(var.tag_keys, local._ctx.tag_keys) tag_values = merge(var.tag_values, local._ctx.tag_values) tag_vars = { - projects = try(local._ctx.tag_vars.projects, {}) - organization = merge({ - for k, v in var.tag_keys : k => v.namespaced_name - if v.allowed_values_regex != null - }, try(local._ctx.tag_vars.organization, {})) + organization = merge( + var.tag_vars.organization, local._ctx.tag_vars.organization + ) + projects = merge( + var.tag_vars.projects, local._ctx.tag_vars.projects + ) } vpc_sc_perimeters = merge(var.perimeters, local._ctx.vpc_sc_perimeters) }) diff --git a/fast/stages/2-security/variables-fast.tf b/fast/stages/2-security/variables-fast.tf index 6238f236c..8ec8686fc 100644 --- a/fast/stages/2-security/variables-fast.tf +++ b/fast/stages/2-security/variables-fast.tf @@ -91,12 +91,9 @@ variable "storage_buckets" { variable "tag_keys" { # tfdoc:variable:source 0-org-setup description = "FAST-managed resource manager tag keys." - type = map(object({ - namespaced_name = string - allowed_values_regex = optional(string) - })) - default = {} - nullable = false + type = map(string) + nullable = false + default = {} } variable "tag_values" { @@ -107,6 +104,17 @@ variable "tag_values" { default = {} } +variable "tag_vars" { + # tfdoc:variable:source 0-org-setup + description = "FAST-managed resource manager tag key namespaced names." + type = object({ + projects = optional(map(map(string)), {}) + organization = optional(map(string), {}) + }) + nullable = false + default = {} +} + variable "universe" { # tfdoc:variable:source 0-org-setup description = "GCP universe where to deploy projects. The prefix will be prepended to the project id." diff --git a/fast/stages/2-security/variables.tf b/fast/stages/2-security/variables.tf index 7792dcbbf..981fe5f92 100644 --- a/fast/stages/2-security/variables.tf +++ b/fast/stages/2-security/variables.tf @@ -17,16 +17,20 @@ variable "context" { description = "Context-specific interpolations." type = object({ - condition_vars = optional(map(map(string)), {}) - email_addresses = optional(map(string), {}) - custom_roles = optional(map(string), {}) - folder_ids = optional(map(string), {}) - iam_principals = optional(map(string), {}) - locations = optional(map(string), {}) - project_ids = optional(map(string), {}) - storage_buckets = optional(map(string), {}) - tag_keys = optional(map(string), {}) - tag_values = optional(map(string), {}) + condition_vars = optional(map(map(string)), {}) + email_addresses = optional(map(string), {}) + custom_roles = optional(map(string), {}) + folder_ids = optional(map(string), {}) + iam_principals = optional(map(string), {}) + locations = optional(map(string), {}) + project_ids = optional(map(string), {}) + storage_buckets = optional(map(string), {}) + tag_keys = optional(map(string), {}) + tag_values = optional(map(string), {}) + tag_vars = optional(object({ + projects = optional(map(map(string)), {}) + organization = optional(map(string), {}) + }), {}) vpc_sc_perimeters = optional(map(string), {}) }) default = {} diff --git a/modules/project-factory/README.md b/modules/project-factory/README.md index c65d836a6..77528cfa1 100644 --- a/modules/project-factory/README.md +++ b/modules/project-factory/README.md @@ -894,20 +894,20 @@ compute.disableSerialPortAccess: | name | description | sensitive | |---|---|:---:| -| [folder_ids](outputs.tf#L91) | Folder ids. | | -| [iam_principals](outputs.tf#L96) | IAM principals mappings. | | -| [kms_keys](outputs.tf#L101) | KMS key ids. | | -| [log_buckets](outputs.tf#L106) | Log bucket ids. | | -| [project_ids](outputs.tf#L113) | Project ids. | | -| [project_numbers](outputs.tf#L118) | Project numbers. | | -| [projects](outputs.tf#L125) | Project attributes. | | -| [pubsub_topics](outputs.tf#L130) | PubSub topic ids. | | -| [service_account_emails](outputs.tf#L137) | Service account emails. | | -| [service_account_iam_emails](outputs.tf#L144) | Service account IAM-format emails. | | -| [service_account_ids](outputs.tf#L151) | Service account IDs. | | -| [service_accounts](outputs.tf#L158) | Service account emails. | | -| [service_agents](outputs.tf#L163) | Service agent emails. | | -| [storage_buckets](outputs.tf#L174) | Bucket names. | | +| [folder_ids](outputs.tf#L102) | Folder ids. | | +| [iam_principals](outputs.tf#L107) | IAM principals mappings. | | +| [kms_keys](outputs.tf#L112) | KMS key ids. | | +| [log_buckets](outputs.tf#L117) | Log bucket ids. | | +| [project_ids](outputs.tf#L124) | Project ids. | | +| [project_numbers](outputs.tf#L129) | Project numbers. | | +| [projects](outputs.tf#L136) | Project attributes. | | +| [pubsub_topics](outputs.tf#L141) | PubSub topic ids. | | +| [service_account_emails](outputs.tf#L148) | Service account emails. | | +| [service_account_iam_emails](outputs.tf#L155) | Service account IAM-format emails. | | +| [service_account_ids](outputs.tf#L162) | Service account IDs. | | +| [service_accounts](outputs.tf#L169) | Service account emails. | | +| [service_agents](outputs.tf#L174) | Service agent emails. | | +| [storage_buckets](outputs.tf#L185) | Bucket names. | | ## Tests diff --git a/modules/project-factory/outputs.tf b/modules/project-factory/outputs.tf index cd5496c03..3ea8d3027 100644 --- a/modules/project-factory/outputs.tf +++ b/modules/project-factory/outputs.tf @@ -66,6 +66,17 @@ locals { module.buckets["${k}/${sk}"].name ) } + tag_keys = { + for sk, sv in module.projects[k].tag_keys : sk => sv.id + } + tag_values = { + for sk, sv in module.projects[k].tag_values : sk => sv.id + } + tag_vars = { + for sk, sv in module.projects[k].tag_keys : sk => sv.namespaced_name + # the provider returns allowed_values_regex set to "" not null + if try(sv.allowed_values_regex, "") != "" + } workload_identity_pools = ( module.projects[k].workload_identity_pool_ids )