# 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 local backup vaults to create in this spoke account. Most spoke stacks need exactly one vault, referenced by every job via target_vault_key. Multiple vaults are supported for cases needing separate KMS keys or isolation within the same account. EOT type = map(object({ name_suffix = string kms_key_arn = optional(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 } } variable "jobs" { description = <<-EOT Map of backup jobs. Each entry is a flattened unit covering the plan rule, the resource selection, and an optional cross-account copy action in one place — the common case of one schedule mapping to one selection. If resource_arns is non-empty, it takes precedence over selection_tags for that job's selection (both may still be set; only resource_arns is used if so). EOT type = map(object({ target_vault_key = string schedule = string start_window_minutes = optional(number, 60) completion_window_minutes = optional(number, 180) delete_after_days = optional(number, 35) cold_storage_after_days = optional(number, null) enable_continuous_backup = optional(bool, false) # Selection scope — resource_arns takes precedence over selection_tags # when both are set. resource_arns = optional(list(string), []) not_resource_arns = optional(list(string), []) selection_tags = optional(map(string), {}) # IAM role AWS Backup assumes to run this job. Must already exist in # this account — this module does not create it. See README. iam_role_arn = string # Optional cross-account copy to a hub vault. copy_action = optional(object({ destination_vault_arn = string delete_after_days = optional(number, 90) cold_storage_after_days = optional(number, null) }), null) })) default = {} validation { condition = alltrue([ for k, v in var.jobs : can(regex("^[a-zA-Z0-9_-]+$", k)) ]) error_message = "Job keys must contain only alphanumerics, hyphens, and underscores — they are used to derive plan and selection names." } validation { condition = alltrue([ for k, v in var.jobs : length(v.resource_arns) > 0 || length(v.selection_tags) > 0 ]) error_message = <<-EOT Every job must define either resource_arns or selection_tags. A selection with neither scopes to nothing. EOT } validation { condition = alltrue([ for k, v in var.jobs : length(trimspace(v.iam_role_arn)) > 0 ]) error_message = "Every job must set iam_role_arn — this module does not create or default it." } validation { condition = alltrue([ for k, v in var.jobs : v.cold_storage_after_days == null ? true : (v.delete_after_days - v.cold_storage_after_days) >= 90 ]) error_message = <<-EOT AWS Backup requires at least 90 days between cold_storage_after_days and delete_after_days. Either raise delete_after_days, lower cold_storage_after_days, or leave cold_storage_after_days unset. EOT } }