Dynamic blocks in Terraform 0.12.x
![Terraform](/images/terraform.jpg)
Some time ago I wrote about how to make dynamic blocks in Terraform 0.11.x, that although it solved the problem, it generated others because it wasn’t an official solution and the interpretation by Terraform was not consistent.
The workaround essentially consisted of defining the blocks dynamically using an assignment to a map list. That is, instead of doing this:
resource "aws_codebuild_project" "codebuild" {
artifacts{
type = "${var.codebuild_artifacts_type}"
location = "${var.codebuild_artifacts_location}"
name = "${var.codebuild_artifacts_name}"
namespace_type = "${var.codebuild_artifacts_namespace_type}"
packaging = "${var.codebuild_artifacts_packaging}"
path = "${var.codebuild_artifacts_path}"
}
...
}
It was replaced by this other:
resource "aws_codebuild_project" "codebuild" {
artifacts = ["${local.codebuild_artifacts}"]
...
}
Where local.codebuild_artifacts
built the block from a map list, which depending on the type defined in the variable codebuild_artifacts_type
it created a different block:
locals {
codebuild_artifacts_def = {
"S3" = [{
type = "${var.codebuild_artifacts_type}"
location = "${var.codebuild_artifacts_location}"
name = "${var.codebuild_artifacts_name}"
namespace_type = "${var.codebuild_artifacts_namespace_type}"
packaging = "${var.codebuild_artifacts_packaging}"
path = "${var.codebuild_artifacts_path}"
encryption_disabled = var.codebuild_artifacts_encryption_disabled
}]
"CODEPIPELINE" = [{
type Some time ago I wrote about [how to make dynamic blocks in Terraform 0.11.x] (https://lgallardo.com/en/2018/07/30/dynamic-configuration-blocks-in-terraform/) {: target = "_blank "}, that although it solved the problem generated others because it was not an official solution and the interpretation by Terraform was not consistent. = "${var.codebuild_artifacts_type}"
name = "${var.codebuild_artifacts_name}"
encryption_disabled = var.codebuild_artifacts_encryption_disabled
}]
"NO_ARTIFACTS" = [{
type = "${var.codebuild_artifacts_type}"
}]
}
# Retuned map
codebuild_artifacts = "${local.codebuild_artifacts_def[var.codebuild_artifacts_type]}"
}
Dynamic blocks in Terraform 0.12.x
Terraform 0.12.x proposes dynamic blocks to solve this and other cases related to blocks. To use dynamic blocks you have to use dynamic
and for-each
. For example, for the above definition:
dynamic "artifacts" {
for_each = [local.codebuild_artifacts]
content {
type = artifacts.value.type
encryption_disabled = lookup(artifacts.value, "encryption_disabled", null)
location = lookup(artifacts.value, "location", null)
name = lookup(artifacts.value, "name", null)
namespace_type = lookup(artifacts.value, "namespace_type", null)
packaging = lookup(artifacts.value, "packaging", null)
path = lookup(artifacts.value, "path", null)
}
}
This way the local.codebuild_artifacts
definition is simplify:
locals {
codebuild_artifacts = {
type = var.codebuild_artifacts_type
location = var.codebuild_artifacts_location
name = var.codebuild_artifacts_name
namespace_type = var.codebuild_artifacts_namespace_type
packaging = var.codebuild_artifacts_packaging
path = var.codebuild_artifacts_path
encryption_disabled = var.codebuild_artifacts_encryption_disabled
}
}
Where is the magic?
First, in the null
data type, which tells Terraform to ignore the attribute if the lookup
function doesn’t find the index on the map. Let’s take a look at the the first lines in the content
block, which is what is replaced in the artifacts
block in each iteration:
content {
type = artifacts.value.type
encryption_disabled = lookup(artifacts.value, "encryption_disabled", null)
location = lookup(artifacts.value, "location", null)
...
Here you can see that:
- The attribute
type
is always defined according to the value ofartifacts.value.type
, so it’s not taken from the map. - The attribute
encryption_disabled
is assigned if something likeartifacts.value ['encryption_disabled']
is found in the map. If the index doesn’t exist, it’s assigned tonull
and therefore it won’t be taken into account by Terraform. - And so on for the rest of the attributes, such as
location
.
This allows having definitions of dynamic blocks, but you have to take into account that some values must be defined together. For example, for the S3
type you must define the following variables:
codebuild_artifacts_type
codebuild_artifacts_location
codebuild_artifacts_name
codebuild_artifacts_namespace_type
codebuild_artifacts_packaging
codebuild_artifacts_path
codebuild_artifacts_encryption_disabled
While for NO_ARTIFACTS
just define this one:
codebuild_artifacts_type
On the other hand, the magic is completed by the fact that we are not using an assignment for the a artifacs
block but rather the dynamic block definition. In other words, instead of this:
artifacts = ["${local.codebuild_artifacts}"]
The dynamic block definition is used:
dynamic "artifacts" {
for_each = [local.codebuild_artifacts]
content {
...
}
Leave a Comment