Reputation: 63
I have a set of 9 security group rules that I need to apply to 4 different sources. I wanted to build it as a module so instead of copy/pasting the same block multiple times, I just need to pass the ports and source as variables.
I have tried to create a module that gets the ports as for_each
in a dynamic block and also passes the sources with count
since I failed to provide an additional dynamic block with for_each
also for the sources.
resource "aws_security_group" "test" {
name = "test2"
count = length(var.groups)
vpc_id = var.vpc_id
dynamic "ingress_tcp" {
for_each = var.tcp_ports
content {
from_port = ingress_tcp.value
to_port = ingress_tcp.value
protocol = "tcp"
security_groups = [var.groups[*].id]
}
}
dynamic "ingress_udp" {
for_each = var.udp_ports
content {
from_port = ingress_udp.value
to_port = ingress_udp.value
protocol = "udp"
security_groups = [var.groups[*].id]
}
}
}
module "rules" {
source = "./module/sg"
vpc_id = var.vpc_id
name = "tomer-test"
tcp_ports = var.tcp_ports
udp_ports = var.udp_ports
groups = [var.groups[*].id]
}
variable "groups" {
description = "source groups"
type = "list"
default = [{
name = "Enforcement-STG",
id = "sg-c9db2183abcd"
},
{
name = "Managment-STG",
id = "sg-b0e71dfa123"
}]
}
variable "name" {
type = string
}
variable "vpc_id" {
type = string
default = ""
}
variable "tcp_ports" {
description = "tcp ports to open"
default = [514,1514, 11514, 12514, 6514]
}
variable "udp_ports" {
description = "tcp ports to open"
default = [514,1514, 11514, 12514]
}
I accept the output to build a set of rules per source groups, but the root module fails to invoke the module. The error that I'm currently getting is
terraform plan
Error: Unsupported block type
on module/sg/main.tf line 7, in resource "aws_security_group" "test":
7: dynamic "ingress_tcp" {
Blocks of type "ingress_tcp" are not expected here.
Error: Unsupported block type
on module/sg/main.tf line 16, in resource "aws_security_group" "test":
16: dynamic "ingress_udp" {
Blocks of type "ingress_udp" are not expected here.
Upvotes: 6
Views: 10169
Reputation: 74064
As the error message suggests, what you tried here is not valid because ingress_tcp
is not a block type expected inside an aws_security_group
resource. The correct name for this nested block type is ingress
:
resource "aws_security_group" "test" {
count = length(var.groups)
name = "test2"
vpc_id = var.vpc_id
dynamic "ingress" {
for_each = var.tcp_ports
content {
from_port = ingress.value
to_port = ingress.value
protocol = "tcp"
security_groups = var.groups[*].id
}
}
dynamic "ingress" {
for_each = var.udp_ports
content {
from_port = ingress.value
to_port = ingress.value
protocol = "udp"
security_groups = var.groups[*].id
}
}
}
If you are using Terraform 0.12.6 or later, you may prefer to write this using resource for_each
instead of count
, like this:
resource "aws_security_group" "test" {
for_each = { for g in var.groups : g.name => g }
name = "test2-${each.key}"
vpc_id = var.vpc_id
dynamic "ingress" {
for_each = var.tcp_ports
content {
from_port = ingress.value
to_port = ingress.value
protocol = "tcp"
security_groups = var.groups[*].id
}
}
dynamic "ingress" {
for_each = var.udp_ports
content {
from_port = ingress.value
to_port = ingress.value
protocol = "udp"
security_groups = var.groups[*].id
}
}
}
This will have a similar result to your count
example, but will produce instances with addresses like aws_security_group.test["Enforcement-STG"]
instead of aws_security_group.test[0]
, which means that when you add and remove elements from var.groups
in future Terraform will be able to determine which instance corresponds with each element and only add/remove the individual instances needed.
This map-based resource is likely to be easier to use elsewhere in the configuration too, since you'll be able to easily find the specific security group for each of your symbolic group names.
Upvotes: 5