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 332e4784..ced6b730 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 99cc1d95..c38f6c96 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 27420061..4fccc28c 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 d8fa403d..28d6c6b7 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 522b5d76..6faa1a6a 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 d0289d2b..0ea7fd99 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 c2ee9e77..b0570a62 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 546dc44b..2898d6dc 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 48d11c70..8a390d1f 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 2942497c..6210d4f5 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 d0d1d5de..fcf702e7 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 96ddf4ba..685fecc7 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 9e8ec88c..9b5ccf6e 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 a48170d5..c84c3540 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 f604c27a..8ac002df 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 19f458ba..c63329a9 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 cc054ce4..574b42a8 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 61534b33..86bee1d5 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 a0a3f894..ff181dec 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 ced6b730..42f91801 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 b0570a62..52c69166 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 8a390d1f..643a4e30 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 fcf702e7..908cc2ce 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 9b5ccf6e..435e523a 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 8ac002df..c605deb7 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 574b42a8..065fb5af 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 ff181dec..28e34d56 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 = "" }