generated from cloudposse/terraform-example-module
-
-
Notifications
You must be signed in to change notification settings - Fork 35
/
Copy pathnormalize.tf
165 lines (135 loc) · 7.1 KB
/
normalize.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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
# In this file, we normalize all the rules into full objects with all keys.
# Then we partition the normalized rules for use as either inline or resourced rules.
locals {
# We have var.rules_map as a key-value object where the values are lists of different types.
# For convenience, the ordinary use cases, and ease of understanding, we also have var.rules,
# which is a single list of rules. First thing we do is to combine the 2 into one object.
rules = merge({ _list_ = var.rules }, var.rules_map)
# Note: we have to use [] instead of null for unset lists due to
# https://github.com/hashicorp/terraform/issues/28137
# which was not fixed until Terraform 1.0.0
norm_rules = local.enabled && local.rules != null ? concat(concat([[]], [for k, rules in local.rules : [for i, rule in rules : {
key = coalesce(lookup(rule, "key", null), "${k}[${i}]")
type = rule.type
from_port = rule.from_port
to_port = rule.to_port
protocol = rule.protocol
description = lookup(rule, "description", local.default_rule_description)
# Convert a missing key, a value of null, or a value of empty list to []
cidr_blocks = try(length(rule.cidr_blocks), 0) > 0 ? rule.cidr_blocks : []
ipv6_cidr_blocks = try(length(rule.ipv6_cidr_blocks), 0) > 0 ? rule.ipv6_cidr_blocks : []
prefix_list_ids = try(length(rule.prefix_list_ids), 0) > 0 ? rule.prefix_list_ids : []
source_security_group_id = lookup(rule, "source_security_group_id", null)
security_groups = []
# self conflicts with other arguments, so it must either be
# null or true (in which case we split it out into a separate rule)
self = lookup(rule, "self", null) == true ? true : null
}]])...) : []
# in rule_matrix and inline rules, a single rule can have a list of security groups
norm_matrix = local.enabled && var.rule_matrix != null ? concat(concat([[]], [for i, subject in var.rule_matrix : [for j, rule in subject.rules : {
key = "${coalesce(lookup(subject, "key", null), "_m[${i}]")}#${coalesce(lookup(rule, "key", null), "[${j}]")}"
type = rule.type
from_port = rule.from_port
to_port = rule.to_port
protocol = rule.protocol
description = lookup(rule, "description", local.default_rule_description)
# We tried to be lenient and convert a missing key, a value of null, or a value of empty list to []
# with cidr_blocks = try(length(rule.cidr_blocks), 0) > 0 ? rule.cidr_blocks : []
# but if a list is provided and any value in the list is not available at plan time,
# that formulation causes problems for `count`, so we must forbid keys present with value of null.
cidr_blocks = lookup(subject, "cidr_blocks", [])
ipv6_cidr_blocks = lookup(subject, "ipv6_cidr_blocks", [])
prefix_list_ids = lookup(subject, "prefix_list_ids", [])
source_security_group_id = null
security_groups = lookup(subject, "source_security_group_ids", [])
# self conflicts with other arguments, so it must either be
# null or true (in which case we split it out into a separate rule)
self = lookup(rule, "self", null) == true ? true : null
}]])...) : []
allow_egress_rule = {
key = "_allow_all_egress_"
type = "egress"
from_port = 0
to_port = 0 # [sic] from and to port ignored when protocol is "-1", warning if not zero
protocol = "-1"
description = "Allow all egress"
cidr_blocks = ["0.0.0.0/0"]
ipv6_cidr_blocks = ["::/0"]
prefix_list_ids = []
self = null
security_groups = []
source_security_group_id = null
}
extra_rules = local.allow_all_egress ? [local.allow_egress_rule] : []
all_inline_rules = concat(local.norm_rules, local.norm_matrix, local.extra_rules)
# For inline rules, the rules have to be separated into ingress and egress
all_ingress_rules = local.inline ? [for r in local.all_inline_rules : r if r.type == "ingress"] : []
all_egress_rules = local.inline ? [for r in local.all_inline_rules : r if r.type == "egress"] : []
# In `aws_security_group_rule` a rule can only have one security group, not a list, so we have to explode the matrix
# Also, self, source_security_group_id, and CIDRs conflict with each other, so they have to be separated out.
# We must be very careful not to make the computed number of rules in any way dependant
# on a computed input value, we must stick to counting things.
self_rules = local.inline ? [] : [for rule in local.norm_matrix : {
key = "${rule.key}#self"
type = rule.type
from_port = rule.from_port
to_port = rule.to_port
protocol = rule.protocol
description = rule.description
cidr_blocks = []
ipv6_cidr_blocks = []
prefix_list_ids = []
self = true
security_groups = []
source_security_group_id = null
# To preserve count and order of rules, we would like to create rules for `false`
# even though they do nothing, but an empty rule is not allowed, so we have to
# create the rule only when `self` is `true`.
# We use `== true` because `self` could be `null` and `if null` is not allowed.
} if rule.self == true]
other_rules = local.inline ? [] : [for rule in local.norm_matrix : {
key = "${rule.key}#cidr"
type = rule.type
from_port = rule.from_port
to_port = rule.to_port
protocol = rule.protocol
description = rule.description
cidr_blocks = rule.cidr_blocks
ipv6_cidr_blocks = rule.ipv6_cidr_blocks
prefix_list_ids = rule.prefix_list_ids
self = null
security_groups = []
source_security_group_id = null
} if length(rule.cidr_blocks) + length(rule.ipv6_cidr_blocks) + length(rule.prefix_list_ids) > 0]
# First, collect all the rules with lists of security groups
sg_rules_lists = local.inline ? [] : [for rule in local.all_inline_rules : {
key = "${rule.key}#sg"
type = rule.type
from_port = rule.from_port
to_port = rule.to_port
protocol = rule.protocol
description = rule.description
cidr_blocks = []
ipv6_cidr_blocks = []
prefix_list_ids = []
self = null
security_groups = rule.security_groups
} if length(rule.security_groups) > 0]
# Now we have to explode the lists into individual rules
sg_exploded_rules = flatten([for rule in local.sg_rules_lists : [for i, sg in rule.security_groups : {
key = "${rule.key}#${i}"
type = rule.type
from_port = rule.from_port
to_port = rule.to_port
protocol = rule.protocol
description = rule.description
cidr_blocks = []
ipv6_cidr_blocks = []
prefix_list_ids = []
self = null
security_groups = []
source_security_group_id = sg
}]])
all_resource_rules = concat(local.norm_rules, local.self_rules, local.sg_exploded_rules, local.other_rules, local.extra_rules)
keyed_resource_rules = { for r in local.all_resource_rules : r.key => r }
}