JUL 05 2018 MARTIN ATKINS
This is the second post of the series highlighting new features in Terraform 0.12.
As part of the lead up to the release of Terraform 0.12, we are publishing a series of feature preview blog posts. The post this week is on first-class expressions.
Terraform uses expressions for dynamic configuration and dependencies. First-class expressions enable variables and operations to be performed outside of strings such as var.foo. In Terraform 0.11 and earlier all expressions were required to be part of interpolations in existing strings, such as "${var.foo}". This simple change has far-ranging benefits, covered in the blog examples.
First-Class Expressions
The example below shows the Terraform 0.11 and earlier syntax:
# Configuration for Terraform 0.11 and earlier
variable "ami" {}
variable "instance_type" {}
variable "vpc_security_group_ids" {
type = "list"
}
resource "aws_instance" "example" {
ami = "${var.ami}"
instance_type = "${var.instance_type}"
vpc_security_group_ids = "${var.vpc_security_group_ids}"
}
The same example converted to Terraform 0.12:
# Configuration for Terraform 0.12
variable "ami" {}
variable "instance_type" {}
variable "vpc_security_group_ids" {
type = "list"
}
resource "aws_instance" "example" {
ami = var.ami
instance_type = var.instance_type
vpc_security_group_ids = var.vpc_security_group_ids
}
Expressions with Lists and Maps
The improvements to expressions in Terraform 0.12 enable using lists and maps more directly with expressions. In versions of Terraform prior to 0.12, the HCL parser supports list and map syntax via [...] and {...} sequences, but it is not possible to use this syntax in conjunction with expressions. This is an artifact of the old HCL implementation having distinct phases for structure and interpolation and was a frequent source of confusion. To work around this limitation, Terraform provides the list and map functions for building lists inside interpolation expressions. The example below shows how this worked in Terraform 0.11 and earlier:
# Configuration for Terraform 0.11 and earlier
resource "aws_instance" "example" {
# …
# The following works because the list structure is static
vpc_security_group_ids = ["${var.security_group_1}", "${var.security_group_2}"]
# The following doesn't work, because the [...] syntax isn't known to the interpolation language
vpc_security_group_ids = "${var.security_group_id != "" ? [var.security_group_id] : []}"
# Instead, it's necessary to use the list() function
vpc_security_group_ids = "${var.security_group_id != "" ? list(var.security_group_id) : list()}"
}
The same example below converted to Terraform 0.12:*
# Configuration for Terraform 0.12
resource "aws_instance" "example" {
# …
vpc_security_group_ids = var.security_group_id != "" ? [var.security_group_id] : []
}
This is also true of maps, allowing the {...} syntax to be used anywhere in an expression. This is generally expected behavior that unfortunately didn't work in previous Terraform releases, but now works as expected in Terraform 0.12.
Repeated Blocks
In 0.12, HCL also no longer accepts the following counter-intuitive configuration:
# Configuration for Terraform 0.11 and earlier
output "weird" {
value = {
foo = "foo"
}
value = {
bar = "bar"
}
}
Due to its attempts to flatten nested block structures down to JSON’s information model, HCL would previously interpret the above as value = [{foo = “foo”},{bar = “bar”}]. While this is a logical consequence of how repeated blocks of the same type behave, it has often caused confusion for users. There are many other tricky behaviors of this sort, which cause the resulting data structure to not conform to the shape given in configuration.
In 0.12, HCL resolves this by making an explicit distinction between attributes and blocks. Attributes are single-assigned values using the assignment operator =. Blocks are repeated maps of key/value configuration pairs.
The above “weird” configuration now produces an error in HCL, because the attribute value may be defined only once. The error message will direct the user to the exact file, line, and column that the error occurred. It is valid to have more than one instance of the same block type:
# Configuration for Terraform 0.12
resource "aws_security_group" "example" {
# …
# "ingress" is parsed as a block because there is no equals sign
ingress {
# ..
}
ingress {
# ..
}
}
Upgrade Guide
Terraform 0.12 will continue to support the string interpolation syntax from prior versions. As noted in the changes above, the use of the assignment operator = with repeated blocks will now produce an error and the = must be omitted. This is a breaking change but will have an intuitive error message pointing directly to the point of the error.
Next
This was part 2 of the blog post series previewing Terraform 0.12.
First-class expressions will be released in Terraform 0.12, coming later this summer. To learn more about how to upgrade to Terraform 0.12, read the upgrade instructions which will be continuously updated as we get closer to releasing Terraform 0.12. If you have any feedback or concerns about these changes, please communicate with the Terraform team via the public mailing list.