From 4e88bec299be78f3fd964fa2d7f673a796c885e0 Mon Sep 17 00:00:00 2001 From: kovagoadam Date: Wed, 10 Dec 2025 18:35:06 +0100 Subject: [PATCH] Use project numbers in billing budget filter (#3555) * Fixed project level billing budget filter * Moved project_numbers local to concat block * Fixed with try block * fix project replacement * tfdoc * fix test --------- Co-authored-by: Ludovico Magnocavallo --- modules/billing-account/README.md | 10 +++++----- modules/billing-account/budgets.tf | 5 +++-- modules/billing-account/variables.tf | 1 + modules/project-factory/README.md | 10 +++++----- modules/project-factory/budgets.tf | 1 + modules/project-factory/projects.tf | 2 +- modules/project-factory/variables.tf | 1 + tests/modules/billing_account/context.tfvars | 5 ++++- tests/modules/billing_account/context.yaml | 2 +- 9 files changed, 22 insertions(+), 15 deletions(-) diff --git a/modules/billing-account/README.md b/modules/billing-account/README.md index b3950fa15..5f43ef3d4 100644 --- a/modules/billing-account/README.md +++ b/modules/billing-account/README.md @@ -277,17 +277,17 @@ update_rules: | name | description | type | required | default | |---|---|:---:|:---:|:---:| -| [id](variables.tf#L147) | Billing account id. | string | ✓ | | +| [id](variables.tf#L148) | Billing account id. | string | ✓ | | | [budget_notification_channels](variables.tf#L17) | Notification channels used by budget alerts. | map(object({…})) | | {} | | [budgets](variables.tf#L47) | Billing budgets. Notification channels are either keys in corresponding variable, or external ids. | map(object({…})) | | {} | -| [context](variables.tf#L122) | Context-specific interpolations. | object({…}) | | {} | -| [factories_config](variables.tf#L138) | Path to folder containing budget alerts data files. | object({…}) | | {} | +| [context](variables.tf#L122) | Context-specific interpolations. | object({…}) | | {} | +| [factories_config](variables.tf#L139) | Path to folder containing budget alerts data files. | object({…}) | | {} | | [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#L54) | 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)) | | {} | -| [logging_sinks](variables.tf#L152) | Logging sinks to create for the billing account. | map(object({…})) | | {} | -| [projects](variables.tf#L185) | Projects associated with this billing account. | list(string) | | [] | +| [logging_sinks](variables.tf#L153) | Logging sinks to create for the billing account. | map(object({…})) | | {} | +| [projects](variables.tf#L186) | Projects associated with this billing account. | list(string) | | [] | ## Outputs diff --git a/modules/billing-account/budgets.tf b/modules/billing-account/budgets.tf index aaa38ae1a..9fab983c4 100644 --- a/modules/billing-account/budgets.tf +++ b/modules/billing-account/budgets.tf @@ -98,8 +98,9 @@ resource "google_billing_budget" "default" { } projects = concat( [ - for v in each.value.filter.projects : - lookup(local.ctx.project_ids, v, v) + for v in each.value.filter.projects : try( + try("projects/${local.ctx.project_numbers[v]}", v), v + ) ], lookup(local.ctx.project_sets, "$project_sets:${each.key}", []) ) diff --git a/modules/billing-account/variables.tf b/modules/billing-account/variables.tf index 061767540..6da7c29b6 100644 --- a/modules/billing-account/variables.tf +++ b/modules/billing-account/variables.tf @@ -130,6 +130,7 @@ variable "context" { project_ids = optional(map(string), {}) project_sets = optional(map(list(string)), {}) storage_buckets = optional(map(string), {}) + project_numbers = optional(map(string), {}) }) default = {} nullable = false diff --git a/modules/project-factory/README.md b/modules/project-factory/README.md index 685cba01a..56c47f0f0 100644 --- a/modules/project-factory/README.md +++ b/modules/project-factory/README.md @@ -802,11 +802,11 @@ compute.disableSerialPortAccess: | name | description | type | required | default | |---|---|:---:|:---:|:---:| -| [factories_config](variables.tf#L161) | Path to folder with YAML resource description data files. | object({…}) | ✓ | | -| [context](variables.tf#L17) | Context-specific interpolations. | object({…}) | | {} | -| [data_defaults](variables.tf#L38) | Optional default values used when corresponding project or folder data from files are missing. | object({…}) | | {} | -| [data_merges](variables.tf#L103) | 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#L122) | Optional values that override corresponding data from files. Takes precedence over file data and `data_defaults`. | object({…}) | | {} | +| [factories_config](variables.tf#L162) | Path to folder with YAML resource description data files. | object({…}) | ✓ | | +| [context](variables.tf#L17) | Context-specific interpolations. | object({…}) | | {} | +| [data_defaults](variables.tf#L39) | Optional default values used when corresponding project or folder data from files are missing. | object({…}) | | {} | +| [data_merges](variables.tf#L104) | 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#L123) | 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/budgets.tf b/modules/project-factory/budgets.tf index c7466a2db..f1648ab12 100644 --- a/modules/project-factory/budgets.tf +++ b/modules/project-factory/budgets.tf @@ -50,6 +50,7 @@ module "billing-budgets" { for v in local.budget_project_sets : v.budget => "projects/${local.outputs_projects[v.project].number}"... } + project_numbers = local.ctx_project_numbers }) factories_config = { budgets_data_path = var.factories_config.budgets.data diff --git a/modules/project-factory/projects.tf b/modules/project-factory/projects.tf index 7a06a2102..d2e5abe46 100644 --- a/modules/project-factory/projects.tf +++ b/modules/project-factory/projects.tf @@ -48,7 +48,7 @@ locals { trimsuffix(f, ".yaml") => yamldecode(file("${local._templates_path}/${f}")) } ctx_project_ids = merge(local.ctx.project_ids, local.project_ids) - ctx_project_numbers = merge(local.ctx.project_ids, local.project_numbers) + ctx_project_numbers = merge(local.ctx.project_numbers, local.project_numbers) project_ids = { for k, v in module.projects : k => v.project_id } diff --git a/modules/project-factory/variables.tf b/modules/project-factory/variables.tf index 6ec7ba406..64b471889 100644 --- a/modules/project-factory/variables.tf +++ b/modules/project-factory/variables.tf @@ -27,6 +27,7 @@ variable "context" { log_buckets = optional(map(string), {}) notification_channels = optional(map(string), {}) project_ids = optional(map(string), {}) + project_numbers = optional(map(string), {}) tag_values = optional(map(string), {}) vpc_host_projects = optional(map(string), {}) vpc_sc_perimeters = optional(map(string), {}) diff --git a/tests/modules/billing_account/context.tfvars b/tests/modules/billing_account/context.tfvars index 4749ed4f8..4df134323 100644 --- a/tests/modules/billing_account/context.tfvars +++ b/tests/modules/billing_account/context.tfvars @@ -9,7 +9,7 @@ budgets = { period = { calendar = "MONTH" } - projects = ["$project_ids:foo"] + projects = ["$project_numbers:foo"] resource_ancestors = ["$folder_ids:bar"] } threshold_rules = [ @@ -48,6 +48,9 @@ context = { project_ids = { foo = "test-prj-foo" } + project_numbers = { + foo = 1234567890 + } } iam = { "$custom_roles:myrole_one" = [ diff --git a/tests/modules/billing_account/context.yaml b/tests/modules/billing_account/context.yaml index a4626bccb..965b638c6 100644 --- a/tests/modules/billing_account/context.yaml +++ b/tests/modules/billing_account/context.yaml @@ -73,7 +73,7 @@ values: credit_types_treatment: INCLUDE_ALL_CREDITS custom_period: [] projects: - - test-prj-foo + - projects/1234567890 resource_ancestors: - folders/1234567890 display_name: 100 dollars in current spend