Reputation: 83
I am new to terraform and trying to create an AWS security group with ingress and egress rules. Rather than hardcoding the values and creating multiple ingress and egress blocks, I am trying to make use of terraform lookup
function.
main.tf
file looks like this:
provider "aws" {
version = "~> 2.0"
region = var.region
profile = var.profile
}
resource "aws_security_group" "this" {
name = "test-sg"
description = "test security group"
dynamic "ingress" {
for_each = var.ingress_rules
content {
description = lookup(ingress.value, "description", null)
from_port = lookup(ingress.value, "from_port", null)
to_port = lookup(ingress.value, "to_port", null)
protocol = lookup(ingress.value, "protocol", null)
cidr_blocks = lookup(ingress.value, "cidr_blocks", null)
}
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "test-sg"
}
}
variables.tf
file looks like this
variable "ingress_rules" {
default = {
"description" = ["For HTTP", "For SSH"]
"from_port" = ["80", "22"]
"to_port" = ["80", "22"]
"protocol" = ["tcp", "tcp"]
"cidr_blocks" = ["0.0.0.0/0", "0.0.0.0/0"]
}
type = map(list(string))
description = "Security group rules"
}
When I run terraform validate
it shows configuration is valid, but when I run terraform plan
, it shows the following error:
ingress.value is list of string with 2 elements
Invalid value for "inputMap" parameter: lookup() requires a map as the first
argument.
After spending a long time still, I am not able to figure out how to solve this error. What is the correct way to pass lookup values to variables.tf
file.
Upvotes: 5
Views: 8566
Reputation: 6836
I would implement it as follows:
resource "aws_security_group" "test_security_group" {
name = "test-sg"
description = "test security group"
dynamic "ingress" {
for_each = var.sg_ingress_rules
content {
from_port = ingress.value.from_port
to_port = ingress.value.to_port
protocol = ingress.value.protocol
cidr_blocks = ingress.value.cidr_blocks
description = ingress.value.description
}
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "test security group"
}
}
variables.tf
variable "sg_ingress_rules" {
description = "Ingress security group rules"
type = map
}
my_vars.tfvars
sg_ingress_rules = {
"1" = {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
description = "HTTP"
},
"2" = {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["<my_private_ip>/32"]
description = "SSH"
}
}
Hope it helps to understand further!
Upvotes: 2
Reputation: 28864
You have constructed your variable's default value as five maps with a string key and list of strings value. You probably wanted a single map with a series of keys and values associated with the various attributes of your ingress rule. You can update the variable value accordingly like:
variable "ingress_rules" {
default = {
"my ingress rule" = {
"description" = "For HTTP"
"from_port" = "80"
"to_port" = "80"
"protocol" = "tcp"
"cidr_blocks" = ["0.0.0.0/0"]
},
"my other ingress rule" = {
"description" = "For SSH"
"from_port" = "22"
"to_port" = "22"
"protocol" = "tcp"
"cidr_blocks" = ["0.0.0.0/0"]
}
}
type = map(any)
description = "Security group rules"
}
Now, in your for_each
iterator, the value of the first ingress.key
will be my ingress rule
, and the value of the first ingress.value
will be your entire map of keys and strings. Before, the first ingress.key
would have been description
, and the first value would have been ["For HTTP", "For SSH"]
. That is why you were getting that error: you cannot lookup a value with key description
from a list of ["For HTTP", "For SSH"]
. After making this variable value update, you should have your expected behavior.
Note that you can refine your type further with an object:
default = {
"my ingress rule" = {
description = "For HTTP"
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
},
"my other ingress rule" = {
description = "For SSH"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
}
type = map(object({
description = string
from_port = number
to_port = number
protocol = string
cidr_blocks = list(string)
}))
Upvotes: 4