I am trying to use the attributes as blocks feature in Terraform 0.12.
If I build the following script, Terraform will configure the security group.
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
}
resource "aws_security_group" "allow_tls" {
name = "allow_tls"
description = "Allow TLS inbound traffic"
vpc_id = aws_vpc.main.id
ingress {
from_port = 443
to_port = 443
protocol = "tcp"
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
If I convert the ingress attribute into block format:
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
}
resource "aws_security_group" "allow_tls" {
name = "allow_tls"
description = "Allow TLS inbound traffic"
vpc_id = aws_vpc.main.id
ingress = [{
from_port = 443
to_port = 443
protocol = "tcp"
}]
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
Terraform returns an error
Error: Incorrect attribute value type
on main.tf line 15, in resource "aws_security_group" "allow_tls":
15: ingress = [{
16: from_port = 443
17: to_port = 443
18: protocol = "tcp"
19: }]
Inappropriate value for attribute "ingress": element 0: attributes
"cidr_blocks", "description", "ipv6_cidr_blocks", "prefix_list_ids",
"security_groups", and "self" are required.
Is this expected?
1 Like
Hi @omerosaienni,
This is indeed the intended behavior, because when you specify it as an attribute the configuration language runtime must use normal type constraint checking to validate the value, rather than block/argument validation. In this case, the ingress
argument is defined as being of an object type, and so the value you provide must conform to that type constraint. The type checker requires that an object can only match an object type constrant if it has a value for all of the attributes of that object type.
With that said, the intended purpose of the “attributes as blocks” behavior is not to write out actual values using that syntax. Instead, it’s to preserve a particular quirky design pattern that some providers were using in Terraform 0.11 and prior where a name that is normally considered to be a block would be treated differently if used as an attribute with an explicit empty list value:
ingress = []
The resource types that follow this pattern generally consider the absence of any ingress
blocks as a request to ignore whatever ingress blocks exist and let them be managed elsewhere. Setting it explicitly to []
, then, is a loophole to allow the user to explicitly ask Terraform to destroy any existing ingress
rules, rather than ignoring them. For that reason, in Terraform’s native syntax []
is the only value that should be used with the attribute syntax; actual ingress
blocks should be specified using the block syntax as normal, which then allows Terraform to use the block/argument validation rules, rather than only the type checker.
(The rules are a little different for the JSON syntax due to the ambiguity of that syntax, but you’re using native syntax in this example so I won’t get into those details here.)
Is this still the case with v1.0?
The docs examples show actual ingress & egress rules inside []
https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group
3 Likes