From 96b900b46d2d8c84a1fe7662ee98c3ea73c42f6d Mon Sep 17 00:00:00 2001 From: Cristian Magherusan-Stanciu Date: Fri, 9 Oct 2026 18:29:34 +0200 Subject: [PATCH 1/2] feat(iac): wire optional Archera comparison settings with single-secret access Adds default-empty archera_org_id, archera_plan_id and a secret reference variable to the AWS (lambda, fargate), GCP (cloud-run, gke) and Azure (container-apps, aks) compute modules and the three environments, passed as ARCHERA_ORG_ID, ARCHERA_PLAN_ID and ARCHERA_API_KEY_SECRET only when set. AWS and GCP grant read on that one secret; Azure adds no grant because the platform vault already carries the vault-wide Secrets User role. The operator creates the secret out-of-band. Documents the setup in DEPLOYMENT.md. Refs #785 #786 Co-Authored-By: Claude Sonnet 5.5 --- docs/DEPLOYMENT.md | 20 +++++++++++++ terraform/environments/aws/compute.tf | 8 ++++++ terraform/environments/aws/variables.tf | 18 ++++++++++++ terraform/environments/azure/compute.tf | 14 ++++++++-- terraform/environments/azure/variables.tf | 18 ++++++++++++ terraform/environments/gcp/compute.tf | 8 ++++++ terraform/environments/gcp/variables.tf | 18 ++++++++++++ terraform/modules/compute/aws/fargate/main.tf | 18 ++++++++++++ .../modules/compute/aws/fargate/variables.tf | 21 ++++++++++++++ terraform/modules/compute/aws/lambda/main.tf | 13 +++++++++ .../modules/compute/aws/lambda/variables.tf | 21 ++++++++++++++ terraform/modules/compute/azure/aks/main.tf | 18 ++++++++++++ .../modules/compute/azure/aks/variables.tf | 21 ++++++++++++++ .../compute/azure/container-apps/main.tf | 11 ++++++++ .../compute/azure/container-apps/variables.tf | 21 ++++++++++++++ .../modules/compute/gcp/cloud-run/main.tf | 21 ++++++++++++++ .../compute/gcp/cloud-run/variables.tf | 21 ++++++++++++++ terraform/modules/compute/gcp/gke/main.tf | 28 +++++++++++++++++++ .../modules/compute/gcp/gke/variables.tf | 21 ++++++++++++++ 19 files changed, 336 insertions(+), 3 deletions(-) diff --git a/docs/DEPLOYMENT.md b/docs/DEPLOYMENT.md index 332e47841..ced6b730f 100644 --- a/docs/DEPLOYMENT.md +++ b/docs/DEPLOYMENT.md @@ -301,6 +301,26 @@ gcloud compute url-maps invalidate-cdn-cache cudly-url-map --path "/*" --- +## Archera insured-commitment comparison (optional) + +The explicit "Compare with Archera" feature is off by default. It is enabled only when all three settings are set; with none set, the platform makes no Archera request. + +| Terraform variable (environment) | Runtime setting | Meaning | +|---|---|---| +| `archera_org_id` | `ARCHERA_ORG_ID` | Archera organization UUID | +| `archera_plan_id` | `ARCHERA_PLAN_ID` | Archera commitment plan UUID (an Archera ID, not a CUDly plan ID) | +| `archera_api_key_secret_arn` (AWS), `archera_api_key_secret_id` (GCP), `archera_api_key_secret_name` (Azure) | `ARCHERA_API_KEY_SECRET` | Reference to the secret holding the Archera API key | + +Create the secret yourself, outside Terraform, so the key never enters Terraform state; Terraform only passes the reference. The platform resolves the key lazily on the first comparison request through the configured secret provider. + +Runtime access is scoped to that one secret: + +- AWS (Lambda and Fargate): `secretsmanager:GetSecretValue` on the secret ARN only. +- GCP (Cloud Run and GKE): a per-secret `roles/secretmanager.secretAccessor` binding on that secret only. +- Azure (Container Apps and AKS): no new role assignment. The secret lives in the platform Key Vault, where the runtime identity already holds the vault-wide Key Vault Secrets User role. For a secret in a different vault, add a secret-scoped role assignment for the runtime identity on that secret; narrowing the vault-wide grant is a separate owner decision. + +Disclosure: the comparison shows Archera's own figures as returned by its API. Amounts carry no currency (Archera provides none), premiums are already included in totals, and an unknown value is shown as unknown, never as zero. Products Archera does not document as supported are shown as "not known to be supported by Archera". Nothing in this view is a guarantee or a purchase; the existing Archera disclosures stay in place. + ## Deploying Code Updates When infrastructure already exists and you only need to update application code: diff --git a/terraform/environments/aws/compute.tf b/terraform/environments/aws/compute.tf index 99cc1d951..c38f6c968 100644 --- a/terraform/environments/aws/compute.tf +++ b/terraform/environments/aws/compute.tf @@ -85,6 +85,10 @@ module "compute_lambda" { fire_scheduled_purchases_schedule = var.fire_scheduled_purchases_schedule # Additional environment variables + archera_org_id = var.archera_org_id + archera_plan_id = var.archera_plan_id + archera_api_key_secret_arn = var.archera_api_key_secret_arn + additional_env_vars = merge( { STATIC_DIR = "/app/static" @@ -208,6 +212,10 @@ module "compute_fargate" { enable_execute_command = var.fargate_enable_execute_command # Additional environment variables + archera_org_id = var.archera_org_id + archera_plan_id = var.archera_plan_id + archera_api_key_secret_arn = var.archera_api_key_secret_arn + additional_env_vars = merge( { STATIC_DIR = "/app/static" diff --git a/terraform/environments/aws/variables.tf b/terraform/environments/aws/variables.tf index 274200610..4fccc28c2 100644 --- a/terraform/environments/aws/variables.tf +++ b/terraform/environments/aws/variables.tf @@ -532,3 +532,21 @@ variable "enable_docker_build" { type = bool default = true } + +variable "archera_org_id" { + description = "Archera organization UUID for the explicit insured-commitment comparison. Empty disables it." + type = string + default = "" +} + +variable "archera_plan_id" { + description = "Archera commitment plan UUID for the comparison. Empty disables it." + type = string + default = "" +} + +variable "archera_api_key_secret_arn" { + description = "ARN of the Secrets Manager secret holding the Archera API key (created out-of-band). Empty disables the Archera comparison." + type = string + default = "" +} diff --git a/terraform/environments/azure/compute.tf b/terraform/environments/azure/compute.tf index d8fa403d0..28d6c6b74 100644 --- a/terraform/environments/azure/compute.tf +++ b/terraform/environments/azure/compute.tf @@ -79,6 +79,10 @@ module "compute_container_apps" { auto_migrate = var.auto_migrate admin_email = var.admin_email admin_password_secret_name = coalesce(module.secrets.admin_password_secret_name, "") + archera_org_id = var.archera_org_id + archera_plan_id = var.archera_plan_id + archera_api_key_secret_name = var.archera_api_key_secret_name + additional_env_vars = merge( { STATIC_DIR = "/app/static" @@ -160,9 +164,13 @@ module "compute_aks" { key_vault_uri = module.secrets.key_vault_uri # Application configuration - admin_email = var.admin_email - admin_password_secret_name = coalesce(module.secrets.admin_password_secret_name, "") - auto_migrate = var.auto_migrate + admin_email = var.admin_email + admin_password_secret_name = coalesce(module.secrets.admin_password_secret_name, "") + auto_migrate = var.auto_migrate + archera_org_id = var.archera_org_id + archera_plan_id = var.archera_plan_id + archera_api_key_secret_name = var.archera_api_key_secret_name + additional_env_vars = merge( { STATIC_DIR = "/app/static" diff --git a/terraform/environments/azure/variables.tf b/terraform/environments/azure/variables.tf index 522b5d762..6faa1a6ab 100644 --- a/terraform/environments/azure/variables.tf +++ b/terraform/environments/azure/variables.tf @@ -558,3 +558,21 @@ variable "max_account_parallelism" { type = number default = 10 } + +variable "archera_org_id" { + description = "Archera organization UUID for the explicit insured-commitment comparison. Empty disables it." + type = string + default = "" +} + +variable "archera_plan_id" { + description = "Archera commitment plan UUID for the comparison. Empty disables it." + type = string + default = "" +} + +variable "archera_api_key_secret_name" { + description = "Name of the platform Key Vault secret holding the Archera API key (created out-of-band). Empty disables the Archera comparison." + type = string + default = "" +} diff --git a/terraform/environments/gcp/compute.tf b/terraform/environments/gcp/compute.tf index d0289d2bf..0ea7fd998 100644 --- a/terraform/environments/gcp/compute.tf +++ b/terraform/environments/gcp/compute.tf @@ -102,6 +102,10 @@ module "compute_cloud_run" { billing_account_id = var.billing_account_id # Additional environment variables + archera_org_id = var.archera_org_id + archera_plan_id = var.archera_plan_id + archera_api_key_secret_id = var.archera_api_key_secret_id + additional_env_vars = merge( { STATIC_DIR = "/app/static" @@ -183,6 +187,10 @@ module "compute_gke" { credential_encryption_key = module.secrets.credential_encryption_key_secret_id } + archera_org_id = var.archera_org_id + archera_plan_id = var.archera_plan_id + archera_api_key_secret_id = var.archera_api_key_secret_id + additional_env_vars = merge( { STATIC_DIR = "/app/static" diff --git a/terraform/environments/gcp/variables.tf b/terraform/environments/gcp/variables.tf index c2ee9e771..b0570a623 100644 --- a/terraform/environments/gcp/variables.tf +++ b/terraform/environments/gcp/variables.tf @@ -499,3 +499,21 @@ variable "max_account_parallelism" { type = number default = 10 } + +variable "archera_org_id" { + description = "Archera organization UUID for the explicit insured-commitment comparison. Empty disables it." + type = string + default = "" +} + +variable "archera_plan_id" { + description = "Archera commitment plan UUID for the comparison. Empty disables it." + type = string + default = "" +} + +variable "archera_api_key_secret_id" { + description = "Secret Manager secret ID holding the Archera API key (created out-of-band). Empty disables the Archera comparison." + type = string + default = "" +} diff --git a/terraform/modules/compute/aws/fargate/main.tf b/terraform/modules/compute/aws/fargate/main.tf index 546dc44b0..2898d6dce 100644 --- a/terraform/modules/compute/aws/fargate/main.tf +++ b/terraform/modules/compute/aws/fargate/main.tf @@ -158,6 +158,8 @@ resource "aws_iam_role_policy" "task_secrets" { var.credential_encryption_key_secret_arn != "" ? "${var.credential_encryption_key_secret_arn}*" : "", var.scheduled_task_secret_arn, var.scheduled_task_secret_arn != "" ? "${var.scheduled_task_secret_arn}*" : "", + var.archera_api_key_secret_arn, + var.archera_api_key_secret_arn != "" ? "${var.archera_api_key_secret_arn}*" : "", ]) }, { @@ -645,6 +647,12 @@ resource "aws_ecs_task_definition" "main" { value = var.scheduled_task_secret_name } ], + [ + for k, v in local.archera_env : { + name = k + value = v + } + ], [ for k, v in var.additional_env_vars : { name = k @@ -1263,3 +1271,13 @@ resource "aws_iam_role_policy" "eventbridge_fire_scheduled_purchases" { ] }) } + +locals { + # Archera settings, set only when non-empty so an unconfigured deployment + # carries no ARCHERA_* variable at all (feature off). + archera_env = merge( + var.archera_org_id != "" ? { ARCHERA_ORG_ID = var.archera_org_id } : {}, + var.archera_plan_id != "" ? { ARCHERA_PLAN_ID = var.archera_plan_id } : {}, + var.archera_api_key_secret_arn != "" ? { ARCHERA_API_KEY_SECRET = var.archera_api_key_secret_arn } : {}, + ) +} diff --git a/terraform/modules/compute/aws/fargate/variables.tf b/terraform/modules/compute/aws/fargate/variables.tf index 48d11c706..8a390d1f6 100644 --- a/terraform/modules/compute/aws/fargate/variables.tf +++ b/terraform/modules/compute/aws/fargate/variables.tf @@ -327,3 +327,24 @@ variable "email_from_domain" { type = string default = "" } + +# Archera insured-commitment comparison (default off; see docs/archera-comparison.md). +# All three must be set for the feature to be configured; the key itself is never +# managed here, the operator creates the secret out-of-band. +variable "archera_org_id" { + description = "Archera organization UUID, passed as ARCHERA_ORG_ID. Empty disables the comparison." + type = string + default = "" +} + +variable "archera_plan_id" { + description = "Archera commitment plan UUID, passed as ARCHERA_PLAN_ID. Empty disables the comparison." + type = string + default = "" +} + +variable "archera_api_key_secret_arn" { + description = "ARN of the Secrets Manager secret holding the Archera API key. Passed as ARCHERA_API_KEY_SECRET and granted GetSecretValue on this one secret only. Empty disables the comparison." + type = string + default = "" +} diff --git a/terraform/modules/compute/aws/lambda/main.tf b/terraform/modules/compute/aws/lambda/main.tf index 2942497ce..6210d4f5f 100644 --- a/terraform/modules/compute/aws/lambda/main.tf +++ b/terraform/modules/compute/aws/lambda/main.tf @@ -90,6 +90,7 @@ resource "aws_lambda_function" "main" { var.purchase_approved_reap_after != "" ? { PURCHASE_APPROVED_REAP_AFTER = var.purchase_approved_reap_after } : {}, + local.archera_env, var.additional_env_vars ) } @@ -260,6 +261,8 @@ resource "aws_iam_role_policy" "secrets_access" { var.credential_encryption_key_secret_arn != "" ? "${var.credential_encryption_key_secret_arn}*" : "", var.scheduled_task_secret_arn, var.scheduled_task_secret_arn != "" ? "${var.scheduled_task_secret_arn}*" : "", + var.archera_api_key_secret_arn, + var.archera_api_key_secret_arn != "" ? "${var.archera_api_key_secret_arn}*" : "", ]) }, { @@ -801,3 +804,13 @@ resource "aws_lambda_permission" "eventbridge_fire_scheduled_purchases" { principal = "events.amazonaws.com" source_arn = aws_cloudwatch_event_rule.fire_scheduled_purchases[0].arn } + +locals { + # Archera settings, set only when non-empty so an unconfigured deployment + # carries no ARCHERA_* variable at all (feature off). + archera_env = merge( + var.archera_org_id != "" ? { ARCHERA_ORG_ID = var.archera_org_id } : {}, + var.archera_plan_id != "" ? { ARCHERA_PLAN_ID = var.archera_plan_id } : {}, + var.archera_api_key_secret_arn != "" ? { ARCHERA_API_KEY_SECRET = var.archera_api_key_secret_arn } : {}, + ) +} diff --git a/terraform/modules/compute/aws/lambda/variables.tf b/terraform/modules/compute/aws/lambda/variables.tf index d0d1d5de2..fcf702e76 100644 --- a/terraform/modules/compute/aws/lambda/variables.tf +++ b/terraform/modules/compute/aws/lambda/variables.tf @@ -307,3 +307,24 @@ variable "tags" { type = map(string) default = {} } + +# Archera insured-commitment comparison (default off; see docs/archera-comparison.md). +# All three must be set for the feature to be configured; the key itself is never +# managed here, the operator creates the secret out-of-band. +variable "archera_org_id" { + description = "Archera organization UUID, passed as ARCHERA_ORG_ID. Empty disables the comparison." + type = string + default = "" +} + +variable "archera_plan_id" { + description = "Archera commitment plan UUID, passed as ARCHERA_PLAN_ID. Empty disables the comparison." + type = string + default = "" +} + +variable "archera_api_key_secret_arn" { + description = "ARN of the Secrets Manager secret holding the Archera API key. Passed as ARCHERA_API_KEY_SECRET and granted GetSecretValue on this one secret only. Empty disables the comparison." + type = string + default = "" +} diff --git a/terraform/modules/compute/azure/aks/main.tf b/terraform/modules/compute/azure/aks/main.tf index 96ddf4bae..685fecc76 100644 --- a/terraform/modules/compute/azure/aks/main.tf +++ b/terraform/modules/compute/azure/aks/main.tf @@ -419,6 +419,14 @@ resource "kubernetes_deployment" "app" { value = "bearer" } + dynamic "env" { + for_each = local.archera_env + content { + name = env.key + value = env.value + } + } + dynamic "env" { for_each = var.additional_env_vars content { @@ -780,3 +788,13 @@ data "kubernetes_service" "nginx_ingress" { time_sleep.wait_for_lb_ip, ] } + +locals { + # Archera settings, set only when non-empty so an unconfigured deployment + # carries no ARCHERA_* variable at all (feature off). + archera_env = merge( + var.archera_org_id != "" ? { ARCHERA_ORG_ID = var.archera_org_id } : {}, + var.archera_plan_id != "" ? { ARCHERA_PLAN_ID = var.archera_plan_id } : {}, + var.archera_api_key_secret_name != "" ? { ARCHERA_API_KEY_SECRET = var.archera_api_key_secret_name } : {}, + ) +} diff --git a/terraform/modules/compute/azure/aks/variables.tf b/terraform/modules/compute/azure/aks/variables.tf index 9e8ec88cc..9b5ccf6e5 100644 --- a/terraform/modules/compute/azure/aks/variables.tf +++ b/terraform/modules/compute/azure/aks/variables.tf @@ -208,3 +208,24 @@ variable "admin_group_object_ids" { type = list(string) default = [] } + +# Archera insured-commitment comparison (default off; see docs/archera-comparison.md). +# All three must be set for the feature to be configured; the key itself is never +# managed here, the operator creates the secret out-of-band. +variable "archera_org_id" { + description = "Archera organization UUID, passed as ARCHERA_ORG_ID. Empty disables the comparison." + type = string + default = "" +} + +variable "archera_plan_id" { + description = "Archera commitment plan UUID, passed as ARCHERA_PLAN_ID. Empty disables the comparison." + type = string + default = "" +} + +variable "archera_api_key_secret_name" { + description = "Name of the Key Vault secret holding the Archera API key, passed as ARCHERA_API_KEY_SECRET. The secret lives in the platform vault, where the runtime identity already holds the vault-wide Key Vault Secrets User role, so no role assignment is added here. Empty disables the comparison." + type = string + default = "" +} diff --git a/terraform/modules/compute/azure/container-apps/main.tf b/terraform/modules/compute/azure/container-apps/main.tf index a48170d5d..c84c35401 100644 --- a/terraform/modules/compute/azure/container-apps/main.tf +++ b/terraform/modules/compute/azure/container-apps/main.tf @@ -141,6 +141,7 @@ resource "azurerm_container_app" "main" { # not emit on the HTTP Connector trigger we use. SCHEDULED_TASK_AUTH_MODE = "bearer" }, + local.archera_env, var.additional_env_vars ) content { @@ -343,3 +344,13 @@ resource "azurerm_monitor_diagnostic_setting" "container_app" { enabled = true } } + +locals { + # Archera settings, set only when non-empty so an unconfigured deployment + # carries no ARCHERA_* variable at all (feature off). + archera_env = merge( + var.archera_org_id != "" ? { ARCHERA_ORG_ID = var.archera_org_id } : {}, + var.archera_plan_id != "" ? { ARCHERA_PLAN_ID = var.archera_plan_id } : {}, + var.archera_api_key_secret_name != "" ? { ARCHERA_API_KEY_SECRET = var.archera_api_key_secret_name } : {}, + ) +} diff --git a/terraform/modules/compute/azure/container-apps/variables.tf b/terraform/modules/compute/azure/container-apps/variables.tf index f604c27a7..8ac002df9 100644 --- a/terraform/modules/compute/azure/container-apps/variables.tf +++ b/terraform/modules/compute/azure/container-apps/variables.tf @@ -258,3 +258,24 @@ variable "ri_exchange_schedule" { type = string default = "0 */6 * * *" } + +# Archera insured-commitment comparison (default off; see docs/archera-comparison.md). +# All three must be set for the feature to be configured; the key itself is never +# managed here, the operator creates the secret out-of-band. +variable "archera_org_id" { + description = "Archera organization UUID, passed as ARCHERA_ORG_ID. Empty disables the comparison." + type = string + default = "" +} + +variable "archera_plan_id" { + description = "Archera commitment plan UUID, passed as ARCHERA_PLAN_ID. Empty disables the comparison." + type = string + default = "" +} + +variable "archera_api_key_secret_name" { + description = "Name of the Key Vault secret holding the Archera API key, passed as ARCHERA_API_KEY_SECRET. The secret lives in the platform vault, where the runtime identity already holds the vault-wide Key Vault Secrets User role, so no role assignment is added here. Empty disables the comparison." + type = string + default = "" +} diff --git a/terraform/modules/compute/gcp/cloud-run/main.tf b/terraform/modules/compute/gcp/cloud-run/main.tf index 19f458ba3..c63329a9e 100644 --- a/terraform/modules/compute/gcp/cloud-run/main.tf +++ b/terraform/modules/compute/gcp/cloud-run/main.tf @@ -173,6 +173,7 @@ resource "google_cloud_run_v2_service" "main" { SCHEDULED_TASK_OIDC_AUDIENCE = local.scheduled_task_oidc_audiences SCHEDULED_TASK_OIDC_SUBJECTS = local.scheduled_task_oidc_subjects }, + local.archera_env, var.additional_env_vars ) content { @@ -521,3 +522,23 @@ resource "google_logging_project_bucket_config" "cloud_run_logs" { retention_days = var.log_retention_days bucket_id = "_Default" } + +locals { + # Archera settings, set only when non-empty so an unconfigured deployment + # carries no ARCHERA_* variable at all (feature off). + archera_env = merge( + var.archera_org_id != "" ? { ARCHERA_ORG_ID = var.archera_org_id } : {}, + var.archera_plan_id != "" ? { ARCHERA_PLAN_ID = var.archera_plan_id } : {}, + var.archera_api_key_secret_id != "" ? { ARCHERA_API_KEY_SECRET = var.archera_api_key_secret_id } : {}, + ) +} + +# Archera API key: one per-secret reader, only when configured. +resource "google_secret_manager_secret_iam_member" "archera_key_reader" { + count = var.archera_api_key_secret_id != "" ? 1 : 0 + + project = var.project_id + secret_id = var.archera_api_key_secret_id + role = "roles/secretmanager.secretAccessor" + member = "serviceAccount:${google_service_account.cloud_run.email}" +} diff --git a/terraform/modules/compute/gcp/cloud-run/variables.tf b/terraform/modules/compute/gcp/cloud-run/variables.tf index cc054ce43..574b42a8d 100644 --- a/terraform/modules/compute/gcp/cloud-run/variables.tf +++ b/terraform/modules/compute/gcp/cloud-run/variables.tf @@ -250,3 +250,24 @@ variable "labels" { type = map(string) default = {} } + +# Archera insured-commitment comparison (default off; see docs/archera-comparison.md). +# All three must be set for the feature to be configured; the key itself is never +# managed here, the operator creates the secret out-of-band. +variable "archera_org_id" { + description = "Archera organization UUID, passed as ARCHERA_ORG_ID. Empty disables the comparison." + type = string + default = "" +} + +variable "archera_plan_id" { + description = "Archera commitment plan UUID, passed as ARCHERA_PLAN_ID. Empty disables the comparison." + type = string + default = "" +} + +variable "archera_api_key_secret_id" { + description = "Secret Manager secret ID (full resource name) holding the Archera API key. Passed as ARCHERA_API_KEY_SECRET and bound with a per-secret secretAccessor grant on this one secret only. Empty disables the comparison." + type = string + default = "" +} diff --git a/terraform/modules/compute/gcp/gke/main.tf b/terraform/modules/compute/gcp/gke/main.tf index 61534b33f..86bee1d52 100644 --- a/terraform/modules/compute/gcp/gke/main.tf +++ b/terraform/modules/compute/gcp/gke/main.tf @@ -557,6 +557,14 @@ resource "kubernetes_deployment" "app" { value = local.scheduled_task_oidc_subject } + dynamic "env" { + for_each = local.archera_env + content { + name = env.key + value = env.value + } + } + dynamic "env" { for_each = var.additional_env_vars content { @@ -810,3 +818,23 @@ resource "google_service_account" "scheduler" { display_name = "Cloud Scheduler service account for GKE ${var.project_name}" project = var.project_id } + +locals { + # Archera settings, set only when non-empty so an unconfigured deployment + # carries no ARCHERA_* variable at all (feature off). + archera_env = merge( + var.archera_org_id != "" ? { ARCHERA_ORG_ID = var.archera_org_id } : {}, + var.archera_plan_id != "" ? { ARCHERA_PLAN_ID = var.archera_plan_id } : {}, + var.archera_api_key_secret_id != "" ? { ARCHERA_API_KEY_SECRET = var.archera_api_key_secret_id } : {}, + ) +} + +# Archera API key: one per-secret reader, only when configured. +resource "google_secret_manager_secret_iam_member" "archera_key_reader" { + count = var.archera_api_key_secret_id != "" ? 1 : 0 + + project = local.secret_project_id + secret_id = var.archera_api_key_secret_id + role = "roles/secretmanager.secretAccessor" + member = "serviceAccount:${google_service_account.workload.email}" +} diff --git a/terraform/modules/compute/gcp/gke/variables.tf b/terraform/modules/compute/gcp/gke/variables.tf index a0a3f8945..ff181dec2 100644 --- a/terraform/modules/compute/gcp/gke/variables.tf +++ b/terraform/modules/compute/gcp/gke/variables.tf @@ -363,3 +363,24 @@ variable "master_authorized_networks" { error_message = "master_authorized_networks must not contain 0.0.0.0/0; that reopens the control plane's public endpoint to the whole internet, exactly what this allowlist exists to prevent." } } + +# Archera insured-commitment comparison (default off; see docs/archera-comparison.md). +# All three must be set for the feature to be configured; the key itself is never +# managed here, the operator creates the secret out-of-band. +variable "archera_org_id" { + description = "Archera organization UUID, passed as ARCHERA_ORG_ID. Empty disables the comparison." + type = string + default = "" +} + +variable "archera_plan_id" { + description = "Archera commitment plan UUID, passed as ARCHERA_PLAN_ID. Empty disables the comparison." + type = string + default = "" +} + +variable "archera_api_key_secret_id" { + description = "Secret Manager secret ID (full resource name) holding the Archera API key. Passed as ARCHERA_API_KEY_SECRET and bound with a per-secret secretAccessor grant on this one secret only. Empty disables the comparison." + type = string + default = "" +} From 4c9d45db1c7d862d8bdf45fe0865299716ddfae3 Mon Sep 17 00:00:00 2001 From: Cristian Magherusan-Stanciu Date: Fri, 9 Oct 2026 19:26:09 +0200 Subject: [PATCH 2/2] fix(iac): take the short GCP secret ID for Archera, quote required disclosures Describe and validate archera_api_key_secret_id as the short secret ID so the per-secret IAM binding (which sets project) is not replaced on every apply, point module variable comments at the real doc, and quote the two required Archera disclosures verbatim in DEPLOYMENT.md. Refs #785 #786 Co-Authored-By: Claude Sonnet 5.5 --- docs/DEPLOYMENT.md | 10 +++++++++- terraform/environments/gcp/variables.tf | 2 +- terraform/modules/compute/aws/fargate/variables.tf | 2 +- terraform/modules/compute/aws/lambda/variables.tf | 2 +- terraform/modules/compute/azure/aks/variables.tf | 2 +- .../modules/compute/azure/container-apps/variables.tf | 2 +- terraform/modules/compute/gcp/cloud-run/variables.tf | 8 ++++++-- terraform/modules/compute/gcp/gke/variables.tf | 8 ++++++-- 8 files changed, 26 insertions(+), 10 deletions(-) diff --git a/docs/DEPLOYMENT.md b/docs/DEPLOYMENT.md index ced6b730f..42f918019 100644 --- a/docs/DEPLOYMENT.md +++ b/docs/DEPLOYMENT.md @@ -319,7 +319,15 @@ Runtime access is scoped to that one secret: - GCP (Cloud Run and GKE): a per-secret `roles/secretmanager.secretAccessor` binding on that secret only. - Azure (Container Apps and AKS): no new role assignment. The secret lives in the platform Key Vault, where the runtime identity already holds the vault-wide Key Vault Secrets User role. For a secret in a different vault, add a secret-scoped role assignment for the runtime identity on that secret; narrowing the vault-wide grant is a separate owner decision. -Disclosure: the comparison shows Archera's own figures as returned by its API. Amounts carry no currency (Archera provides none), premiums are already included in totals, and an unknown value is shown as unknown, never as zero. Products Archera does not document as supported are shown as "not known to be supported by Archera". Nothing in this view is a guarantee or a purchase; the existing Archera disclosures stay in place. +Required disclosures, shown verbatim wherever Archera is offered (pkg/common `ArcheraNonGatingDisclosure` and `ArcheraSponsorshipDisclosure`): + +> This is entirely optional. CUDly's purchase and management features work fully without Archera. +> +> For full disclosure, Archera sponsors CUDly's Open Source development from a fraction of their insurance premiums. + +The GCP secret variable takes the short secret ID, not the `projects//secrets/` resource name: the per-secret binding sets the project, and the full name makes the binding be replaced on every apply. + +The comparison shows Archera's own figures as returned by its API. Amounts carry no currency (Archera provides none), premiums are already included in totals, and an unknown value is shown as unknown, never as zero. Products Archera does not document as supported are shown as "not known to be supported by Archera". Nothing in this view is a guarantee or a purchase; the existing Archera disclosures stay in place. ## Deploying Code Updates diff --git a/terraform/environments/gcp/variables.tf b/terraform/environments/gcp/variables.tf index b0570a623..52c691667 100644 --- a/terraform/environments/gcp/variables.tf +++ b/terraform/environments/gcp/variables.tf @@ -513,7 +513,7 @@ variable "archera_plan_id" { } variable "archera_api_key_secret_id" { - description = "Secret Manager secret ID holding the Archera API key (created out-of-band). Empty disables the Archera comparison." + description = "Short Secret Manager secret ID (not the full resource name) holding the Archera API key (created out-of-band). Empty disables the Archera comparison." type = string default = "" } diff --git a/terraform/modules/compute/aws/fargate/variables.tf b/terraform/modules/compute/aws/fargate/variables.tf index 8a390d1f6..643a4e300 100644 --- a/terraform/modules/compute/aws/fargate/variables.tf +++ b/terraform/modules/compute/aws/fargate/variables.tf @@ -328,7 +328,7 @@ variable "email_from_domain" { default = "" } -# Archera insured-commitment comparison (default off; see docs/archera-comparison.md). +# Archera insured-commitment comparison (default off; see the Archera section of docs/DEPLOYMENT.md). # All three must be set for the feature to be configured; the key itself is never # managed here, the operator creates the secret out-of-band. variable "archera_org_id" { diff --git a/terraform/modules/compute/aws/lambda/variables.tf b/terraform/modules/compute/aws/lambda/variables.tf index fcf702e76..908cc2cea 100644 --- a/terraform/modules/compute/aws/lambda/variables.tf +++ b/terraform/modules/compute/aws/lambda/variables.tf @@ -308,7 +308,7 @@ variable "tags" { default = {} } -# Archera insured-commitment comparison (default off; see docs/archera-comparison.md). +# Archera insured-commitment comparison (default off; see the Archera section of docs/DEPLOYMENT.md). # All three must be set for the feature to be configured; the key itself is never # managed here, the operator creates the secret out-of-band. variable "archera_org_id" { diff --git a/terraform/modules/compute/azure/aks/variables.tf b/terraform/modules/compute/azure/aks/variables.tf index 9b5ccf6e5..435e523a8 100644 --- a/terraform/modules/compute/azure/aks/variables.tf +++ b/terraform/modules/compute/azure/aks/variables.tf @@ -209,7 +209,7 @@ variable "admin_group_object_ids" { default = [] } -# Archera insured-commitment comparison (default off; see docs/archera-comparison.md). +# Archera insured-commitment comparison (default off; see the Archera section of docs/DEPLOYMENT.md). # All three must be set for the feature to be configured; the key itself is never # managed here, the operator creates the secret out-of-band. variable "archera_org_id" { diff --git a/terraform/modules/compute/azure/container-apps/variables.tf b/terraform/modules/compute/azure/container-apps/variables.tf index 8ac002df9..c605deb77 100644 --- a/terraform/modules/compute/azure/container-apps/variables.tf +++ b/terraform/modules/compute/azure/container-apps/variables.tf @@ -259,7 +259,7 @@ variable "ri_exchange_schedule" { default = "0 */6 * * *" } -# Archera insured-commitment comparison (default off; see docs/archera-comparison.md). +# Archera insured-commitment comparison (default off; see the Archera section of docs/DEPLOYMENT.md). # All three must be set for the feature to be configured; the key itself is never # managed here, the operator creates the secret out-of-band. variable "archera_org_id" { diff --git a/terraform/modules/compute/gcp/cloud-run/variables.tf b/terraform/modules/compute/gcp/cloud-run/variables.tf index 574b42a8d..065fb5af9 100644 --- a/terraform/modules/compute/gcp/cloud-run/variables.tf +++ b/terraform/modules/compute/gcp/cloud-run/variables.tf @@ -251,7 +251,7 @@ variable "labels" { default = {} } -# Archera insured-commitment comparison (default off; see docs/archera-comparison.md). +# Archera insured-commitment comparison (default off; see the Archera section of docs/DEPLOYMENT.md). # All three must be set for the feature to be configured; the key itself is never # managed here, the operator creates the secret out-of-band. variable "archera_org_id" { @@ -267,7 +267,11 @@ variable "archera_plan_id" { } variable "archera_api_key_secret_id" { - description = "Secret Manager secret ID (full resource name) holding the Archera API key. Passed as ARCHERA_API_KEY_SECRET and bound with a per-secret secretAccessor grant on this one secret only. Empty disables the comparison." + validation { + condition = !strcontains(var.archera_api_key_secret_id, "/") + error_message = "archera_api_key_secret_id must be the short secret ID, not a projects//secrets/ resource name (the per-secret IAM binding sets project and would be replaced on every apply otherwise)." + } + description = "Short Secret Manager secret ID (not the projects//secrets/ resource name) holding the Archera API key. Passed as ARCHERA_API_KEY_SECRET and bound with a per-secret secretAccessor grant on this one secret only. Empty disables the comparison." type = string default = "" } diff --git a/terraform/modules/compute/gcp/gke/variables.tf b/terraform/modules/compute/gcp/gke/variables.tf index ff181dec2..28e34d56c 100644 --- a/terraform/modules/compute/gcp/gke/variables.tf +++ b/terraform/modules/compute/gcp/gke/variables.tf @@ -364,7 +364,7 @@ variable "master_authorized_networks" { } } -# Archera insured-commitment comparison (default off; see docs/archera-comparison.md). +# Archera insured-commitment comparison (default off; see the Archera section of docs/DEPLOYMENT.md). # All three must be set for the feature to be configured; the key itself is never # managed here, the operator creates the secret out-of-band. variable "archera_org_id" { @@ -380,7 +380,11 @@ variable "archera_plan_id" { } variable "archera_api_key_secret_id" { - description = "Secret Manager secret ID (full resource name) holding the Archera API key. Passed as ARCHERA_API_KEY_SECRET and bound with a per-secret secretAccessor grant on this one secret only. Empty disables the comparison." + validation { + condition = !strcontains(var.archera_api_key_secret_id, "/") + error_message = "archera_api_key_secret_id must be the short secret ID, not a projects//secrets/ resource name (the per-secret IAM binding sets project and would be replaced on every apply otherwise)." + } + description = "Short Secret Manager secret ID (not the projects//secrets/ resource name) holding the Archera API key. Passed as ARCHERA_API_KEY_SECRET and bound with a per-secret secretAccessor grant on this one secret only. Empty disables the comparison." type = string default = "" }