Вопрос о синтаксисе Terraform. Я пытаюсь свести к минимуму логику создания групп безопасности, просто перебирая список локальных переменных, вот пример
locals {
egress = {
udp = [
53,
123
]
}
ingress = {
tcp = [
443,
22,
]
}
}
и ресурс:
resource "aws_security_group_rule" "in_tcp" {
#for_each = [ for k, v in local.ingress : local.ingress.k => v ]
#for_each = [ for port in local.ingress : port => port ]
#for_each = [ for proto in local.ingress : proto ]
#for_each = [ for k, v in local.ingress : k ]
for_each = local.ingress
type = "ingress"
from_port = each.value
to_port = each.value
protocol = tostring(each.key)
self = true
security_group_id = aws_security_group.main.id
}
Я пробовал все, но ничего не работает. Наверное, я не понимаю значения [ for ... ] и { for ... }
На данный момент лучший результат, как указано выше, с использованием простого for_each = local.ingress
, но я получаю сообщение об ошибке:
Error: Incorrect attribute value type
│
│ on sg-rules/main.tf line 38, in resource "aws_security_group_rule" "in_tcp":
│ 38: from_port = each.value
│ ├────────────────
│ │ each.value is tuple with 2 elements
│
│ Inappropriate value for attribute "from_port": number required.
когда я конвертирую каждое значение в число, используя from_port = tonumber(each.value)
, я получаю еще одну ошибку:
│ Error: Invalid function argument
│
│ on sg-rules/main.tf line 38, in resource "aws_security_group_rule" "in_tcp":
│ 38: from_port = tonumber(each.value)
│ ├────────────────
│ │ while calling tonumber(v)
│ │ each.value is tuple with 2 elements
│
│ Invalid value for "v" parameter: cannot convert tuple to number.
Как насчет того, чтобы изменить local, чтобы он был немного более дружелюбным к циклу...
Что-то вроде этого
locals {
ingress = {
443 : "tcp",
22 : "tcp"
}
}
resource "aws_security_group_rule" "in_tcp" {
for_each = local.ingress
type = "ingress"
from_port = each.key
to_port = each.key
protocol = each.value
self = true
security_group_id = "sg-123456"
}
Вывод плана TF теперь выглядит так:
Terraform will perform the following actions:
# aws_security_group_rule.in_tcp["22"] will be created
+ resource "aws_security_group_rule" "in_tcp" {
+ from_port = 22
+ id = (known after apply)
+ protocol = "tcp"
+ security_group_id = "sg-123456"
+ security_group_rule_id = (known after apply)
+ self = true
+ source_security_group_id = (known after apply)
+ to_port = 22
+ type = "ingress"
}
# aws_security_group_rule.in_tcp["443"] will be created
+ resource "aws_security_group_rule" "in_tcp" {
+ from_port = 443
+ id = (known after apply)
+ protocol = "tcp"
+ security_group_id = "sg-123456"
+ security_group_rule_id = (known after apply)
+ self = true
+ source_security_group_id = (known after apply)
+ to_port = 443
+ type = "ingress"
}
Plan: 2 to add, 0 to change, 0 to destroy.
Спасибо, это фантастика. Я хотел, чтобы мой код был простым, но ваш все еще прост и требует только одного ресурса. Однако, поскольку terraform принимает только один уникальный ключ, это означает, что я могу назначить порт только одному протоколу. Я не могу создать порт 53 tcp и udp одновременно. Тем не менее порт 53 не является обычным случаем. Спасибо. С другой стороны, я все еще могу использовать вашу структуру, изменить протокол на блок CIDR и оставить его только для одного протокола. Просто мысль.
Первое: local.ingress
— это объект с ключом = «tcp» и значением = [443, 22]. Второе: foreach
принимает только карту или набор строк. Вам нужна карта, проиндексированная "<proto_port>" следующим образом:
{
"tcp_22" = {
"port" = 22
"proto" = "tcp"
}
"tcp_443" = {
"port" = 443
"proto" = "tcp"
}
}
Вот как вы это делаете:
resource "aws_security_group_rule" "in_tcp" {
for_each = {
for p in flatten([
for proto, ports in local.ingress: [
for port in ports: { proto = proto, port = port }
]
]): "${p.proto}_${p.port}" => p
}
type = "ingress"
from_port = tonumber(each.value.port)
to_port = tonumber(each.value.port)
protocol = tostring(each.value.proto)
self = true
security_group_id = aws_security_group.main.id
}
Как насчет использования строк вместо чисел внутри ваших локальных переменных и использования функции «toset» в блоке for_each. Затем вы создаете aws_security_group_rule для каждого протокола соответственно:
locals {
egress = {
udp = [
"53",
"123"
]
}
ingress = {
tcp = [
"443",
"22",
]
}
}
resource "aws_security_group_rule" "in_tcp" {
for_each = toset(local.ingress.tcp)
type = "ingress"
from_port = tonumber(each.value)
to_port = tonumber(each.value)
protocol = "tcp"
self = true
security_group_id = aws_security_group.main.id
}
resource "aws_security_group_rule" "out_udp" {
for_each = toset(local.egress.udp)
type = "egress"
from_port = tonumber(each.value)
to_port = tonumber(each.value)
protocol = "udp"
self = true
security_group_id = aws_security_group.main.id
}
Это хороший чистый ответ, я добавляю приведенное ниже как дополнение, если структура данных OP не может быть изменена. Ниже приведено выражение, которое может преобразовать существующую структуру данных в структуру, указанную в этом ответе.
merge([for proto, ports in local.ingress: {for port in ports: port=> proto}]...)