# Variables =================================================================== variable "name_prefix" { description = "Prefix prepended to resource names created by this module" type = string validation { condition = length(trimspace(var.name_prefix)) > 0 error_message = "name_prefix must not be empty." } } variable "tags" { description = "Tags applied to all resources in this module." type = map(string) default = {} } variable "vaults" { description = <<-EOT Map of centralized backup vaults to create in the hub account. Each vault carries its own list of trusted spoke role ARNs (principals allowed to backup:CopyIntoBackupVault) so that different vaults can enforce different trust boundaries — e.g. a compliance-tier vault restricted to a subset of spoke accounts. EOT type = map(object({ name_suffix = string kms_key_arn = optional(string, null) # Principals (spoke account backup service roles) allowed to copy # recovery points into this vault. Required — a vault with an empty # list accepts no cross-account copies. trusted_spoke_role_arns = list(string) # Vault lock (WORM compliance mode) lock_enabled = optional(bool, false) lock_changeable_for_days = optional(number, 3) lock_min_retention_days = optional(number, null) lock_max_retention_days = optional(number, null) # Notifications — fully opt-in, no default events fire unless listed. # Set sns_topic_arn to reuse an existing topic; leave null and set # create_sns_topic = true to have the module provision one. notifications = optional(object({ sns_topic_arn = optional(string, null) create_sns_topic = optional(bool, false) kms_key_arn = optional(string, null) backup_vault_events = list(string) }), null) })) default = {} validation { condition = alltrue([ for k, v in var.vaults : can(regex("^[a-zA-Z0-9_-]+$", v.name_suffix)) ]) error_message = <<-EOT Vault name_suffix values must contain only alphanumerics, hyphens, and underscores (no dots, spaces, or other characters). EOT } validation { condition = alltrue([ for k, v in var.vaults : v.lock_enabled ? (v.lock_min_retention_days != null) : true ]) error_message = <<-EOT lock_min_retention_days is required when lock_enabled is true. Set a retention floor, or leave lock_enabled = false for this vault. EOT } validation { condition = alltrue([ for k, v in var.vaults : v.lock_enabled && v.lock_min_retention_days != null ? v.lock_min_retention_days >= 1 : true ]) error_message = "lock_min_retention_days must be at least 1 day." } validation { condition = alltrue([ for k, v in var.vaults : v.lock_enabled && v.lock_max_retention_days != null ? v.lock_max_retention_days >= 1 : true ]) error_message = "lock_max_retention_days must be at least 1 day." } validation { condition = alltrue([ for k, v in var.vaults : (v.lock_enabled && v.lock_min_retention_days != null && v.lock_max_retention_days != null) ? v.lock_max_retention_days >= v.lock_min_retention_days : true ]) error_message = <<-EOT lock_max_retention_days must be greater than or equal to lock_min_retention_days when both are set. EOT } validation { condition = alltrue([ for k, v in var.vaults : v.lock_enabled ? v.lock_changeable_for_days >= 3 : true ]) error_message = <<-EOT lock_changeable_for_days must be at least 3 — AWS Backup enforces a minimum 3-day cooling-off period before compliance mode locks in permanently. EOT } validation { condition = alltrue([ for k, v in var.vaults : length(v.trusted_spoke_role_arns) > 0 ]) error_message = <<-EOT Every hub vault must define at least one trusted_spoke_role_arns entry. A vault with an empty list can never receive cross-account copies. EOT } validation { condition = alltrue([ for k, v in var.vaults : v.notifications == null ? true : ( v.notifications.sns_topic_arn != null || v.notifications.create_sns_topic ) ]) error_message = <<-EOT When notifications is set, you must either: - provide an existing sns_topic_arn, or - set create_sns_topic = true to have the module provision one. EOT } validation { condition = alltrue([ for k, v in var.vaults : v.notifications == null ? true : length(v.notifications.backup_vault_events) > 0 ]) error_message = <<-EOT notifications.backup_vault_events must list at least one event when notifications is configured for a vault. EOT } validation { condition = alltrue(flatten([ for k, v in var.vaults : [ for event in(v.notifications == null ? [] : v.notifications.backup_vault_events) : contains(local.valid_vault_events_for_notifications, event) ] ])) error_message = <<-EOT notifications.backup_vault_events contains an event not recognized by AWS Backup. Valid Events: ${join("\n ", [for chunk in chunklist(local.valid_vault_events_for_notifications, 3) : join(", ", chunk)])} EOT } }