variables.tf
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# 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
  }
}