diff --git a/docs/DEPLOYMENT.md b/docs/DEPLOYMENT.md index 332e47841..42f918019 100644 --- a/docs/DEPLOYMENT.md +++ b/docs/DEPLOYMENT.md @@ -301,6 +301,34 @@ 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. + +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 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..52c691667 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 = "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/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..643a4e300 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 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" { + 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..908cc2cea 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 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" { + 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..435e523a8 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 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" { + 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..c605deb77 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 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" { + 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..065fb5af9 100644 --- a/terraform/modules/compute/gcp/cloud-run/variables.tf +++ b/terraform/modules/compute/gcp/cloud-run/variables.tf @@ -250,3 +250,28 @@ variable "labels" { type = map(string) default = {} } + +# 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" { + 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" { + 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/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..28e34d56c 100644 --- a/terraform/modules/compute/gcp/gke/variables.tf +++ b/terraform/modules/compute/gcp/gke/variables.tf @@ -363,3 +363,28 @@ 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 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" { + 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" { + 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 = "" +}