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'."
}
}
|