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
118
119
120
# 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 "key_pairs" {
  description = <<-EOT
    Map of key pairs to register in AWS EC2.
    Each entry requires a name and an OpenSSH-format public key string
    which may be sourced from a secret store or a TLS provider.
  EOT
  type = map(object({
    name       = string
    public_key = string
  }))
  default = {}
}

variable "instances" {
  description = <<-EOT
    Map of EC2 instance definitions.

    - ami_id is passed in explicitly to ensure deterministic builds.
    - name_suffix is appended to resources to ensure standardized names.
  EOT
  type = map(object({
    name_suffix           = string
    ami_id                = string
    instance_type         = string
    subnet_id             = string
    security_group_ids    = list(string)
    iam_instance_profile  = optional(string)
    key_name              = optional(string)
    associate_public_ip   = optional(bool, false)
    ebs_optimized         = optional(bool, true)
    monitoring            = optional(bool, false)
    placement_group       = optional(string)
    tenancy               = optional(string, "default")
    primary_private_ip    = optional(string)
    secondary_private_ips = optional(list(string), [])

    # User data — mutually exclusive
    user_data        = optional(string)
    user_data_base64 = optional(string)

    # IMDSv2 — tokens required by default, hop limit 1 unless overridden (e.g. ECS needs 2)
    metadata_http_tokens = optional(string, "required")
    metadata_hop_limit   = optional(number, 1)

    # Root volume
    root_volume = object({
      size                  = number
      type                  = optional(string, "gp3")
      kms_key_id            = optional(string)
      delete_on_termination = optional(bool, true)
      tags                  = optional(map(string), {})
    })

    # Additional EBS data volumes
    data_volumes = optional(list(object({
      device_name           = string
      volume_size           = number
      volume_type           = optional(string, "gp3")
      kms_key_id            = optional(string)
      delete_on_termination = optional(bool, true)
      iops                  = optional(number)
      throughput            = optional(number)
      tags                  = optional(map(string), {})
    })), [])

    # Per-instance tags — overrides module level tags
    instance_tags = optional(map(string), {})
  }))
  default = {}

  validation {
    condition = alltrue([
      for k, v in var.instances :
      v.primary_private_ip == null || can(cidrhost("${v.primary_private_ip}/32", 0))
    ])
    error_message = "primary_private_ip must be a valid IPv4 address."
  }

  validation {
    condition = alltrue([
      for k, v in var.instances :
      alltrue([for ip in v.secondary_private_ips : can(cidrhost("${ip}/32", 0))])
    ])
    error_message = "secondary_private_ips must contain valid IPv4 addresses."
  }

  validation {
    condition = alltrue([
      for k, v in var.instances :
      !(v.user_data != null && v.user_data_base64 != null)
    ])
    error_message = "Instances may not set both user_data and user_data_base64. Use one or the other."
  }

  validation {
    condition = alltrue([
      for k, v in var.instances :
      contains(["required", "optional"], v.metadata_http_tokens)
    ])
    error_message = "metadata_http_tokens must be 'required' or 'optional'."
  }
}