I tried to create an AWS security group with multiple inbound rules, Normally we need to multiple ingresses in the sg for multiple inbound rules. Instead of creating multiple ingress rules separately, I tried to create a list of ingress and so that I can easily reuse the module for different applications.
PFB,
module/sg/sg.tf >>
resource "aws_security_group" "ec2_security_groups" {
name = var.name_security_groups
vpc_id = var.vpc_id
}
module/sg/rules.tf >>
resource "aws_security_group_rule" "ingress_rules" {
count = lenght(var.ingress_rules)
type = "ingress"
from_port = var.ingress_rules[count.index][0]
to_port = var.ingress_rules[count.index][1]
protocol = var.ingress_rules[count.index][2]
cidr_blocks = var.ingress_rules[count.index][3]
description = var.ingress_rules[count.index][4]
security_group_id = aws_security_group.ec2_security_groups.id
}
module/sg/variable.tf >>
variable "vpc_id" {
}
variable "name_security_groups" {
}
variable "ingress_rules" {
type = list(string)
}
In the application folder,
application/dev/sg.tf >>
module "sg_test" {
source = "../modules/sg"
vpc_id = "vpc-xxxxxxxxx"
name_security_groups = "sg_test"
ingress_rules = var.sg_ingress_rules
}
application/dev/variable.tf >>
variable "sg_ingress_rules" {
type = list(string)
default = {
[22, 22, "tcp", "1.2.3.4/32", "test"]
[23, 23, "tcp", "1.2.3.4/32", "test"]
}
}
Error:
Error: Missing attribute value
on test-sgs.tf line 21, in variable "sg_ingress_rules":
20:
21:
22:
Expected an attribute value, introduced by an equals sign ("=").
Please help to correct this or if there is any other method please suggest.
Regards,
Thanks@apparentlymart, who helped to solve this in Terraform discussion
The Security rule:-
resource "aws_security_group_rule" "ingress_rules" {
count = length(var.ingress_rules)
type = "ingress"
from_port = var.ingress_rules[count.index].from_port
to_port = var.ingress_rules[count.index].to_port
protocol = var.ingress_rules[count.index].protocol
cidr_blocks = [var.ingress_rules[count.index].cidr_block]
description = var.ingress_rules[count.index].description
security_group_id = aws_security_group.ec2_security_groups.id
}
And the variable:
variable "sg_ingress_rules" {
type = list(object({
from_port = number
to_port = number
protocol = string
cidr_block = string
description = string
}))
default = [
{
from_port = 22
to_port = 22
protocol = "tcp"
cidr_block = "1.2.3.4/32"
description = "test"
},
{
from_port = 23
to_port = 23
protocol = "tcp"
cidr_block = "1.2.3.4/32"
description = "test"
},
]
}
If you want this to work literally with indexed fields, make it a list(list(string)) and change the default oyter syntax from braces (used for maps) to brackets (used for lists):
variable "sg_ingress_rules" {
type = list(list(string))
default = [
[22, 22, "tcp", "1.2.3.4/32", "test"]
[23, 23, "tcp", "1.2.3.4/32", "test"]
]
}
That is a confusing data structure and will be difficult to work with, so I recommend this instead:
variable "sg_ingress_rules" {
type = map(map(any))
default = {
thing1 = {from=22, to=22, proto="tcp", cidr="1.2.3.4/32", desc=test"]
thing2 = {from=23, to=23, proto="tcp", cidr="1.2.3.4/32", desc="test"}
}
}
You can use better names than the terrible ones I've chosen and then refer to them in your resource:
resource "aws_security_group_rule" "ingress_rules" {
for_each = var.ingress_rules
type = "ingress"
from_port = each.value.from
to_port = each.value.to
protocol = each.value.proto
cidr_blocks = each.value.cidr
description = each.value.desc
security_group_id = aws_security_group.ec2_security_groups.id
}
You'll get multiple named copies of the aws_security_group_rule which better survives insertions and deletions from the ingress_rules variable and will save you headaches. Otherwise you'll get superfluous destroys and creates of rules and sometimes conflicts due to the indexed resources a count creates.
If you are feeling like having some better guardrails on people setting the ingress_rules value you can use object to require and restrict to a particular set of fields with certain types as follows:
variable "sg_ingress_rules" {
type = map(object(
{
from = number
to = number
proto = string
cidr = string
desc = string
}
))
default = {
thing1 = {from=22, to=22, proto="tcp", cidr="1.2.3.4/32", desc=test"]
thing2 = {from=23, to=23, proto="tcp", cidr="1.2.3.4/32", desc="test"}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With