Terraform 0.12 was released in May, 2019, and it included a few major changes:
.tfvars
file. In particular, any variable defined in a .tfvars
file
that does not match a corresponding variable
definition in your .tf
files produces an error.${...}
).Before version 0.19.0, Terragrunt had you define its configuration in a terragrunt = { ... }
variable in
a terraform.tfvars
file, but due to item (1) this no longer works with Terraform 0.12 and newer. That means we had to
move to a new file format. This requires a migration, which is unfortunate, but as a nice benefit, item (2)
gives us a nicer syntax and new functionality!
The following sections outline the steps you may need to take in order to migrate from Terragrunt <= v0.18.x to Terragrunt 0.19.x and newer:
Check out this PR in the terragrunt-infrastructure-live-example repo for an example of what the code changes look like.
Since Terraform 0.12 has more strict rules about what can go into terraform.tfvars
files, you now need to move your
Terragrunt configuration from terraform.tfvars
to a terragrunt.hcl
file, removing the terragrunt = { ... }
wrapping along the way.
For example, if you had the following in terraform.tfvars
:
# terraform.tfvars
terragrunt = {
terraform {
source = "git::git@github.com:foo/modules.git//frontend-app?ref=v0.0.3"
extra_arguments "custom_vars" {
commands = ["apply", "plan"]
arguments = ["-var", "foo=42"]
}
}
remote_state {
backend = "s3"
config = {
bucket = "my-terraform-state"
key = "${path_relative_to_include()}/terraform.tfstate"
region = "us-east-1"
encrypt = true
dynamodb_table = "my-lock-table"
}
}
}
You would migrate this to terragrunt.hcl
as follows:
# terragrunt.hcl
terraform {
source = "git::git@github.com:foo/modules.git//frontend-app?ref=v0.0.3"
extra_arguments "custom_vars" {
commands = ["apply", "plan"]
arguments = ["-var", "foo=42"]
}
}
remote_state {
backend = "s3"
config = {
bucket = "my-terraform-state"
key = "${path_relative_to_include()}/terraform.tfstate"
region = "us-east-1"
encrypt = true
dynamodb_table = "my-lock-table"
}
}
When we were using terraform.tfvars
files for Terragrunt configuration, we were piggybacking on the fact that
Terraform automatically loads variables from tfvars
files to set variables
for our modules:
# terraform.tfvars
# Terragrunt configuration
terragrunt = {
terraform {
# ...
}
remote_state {
# ...
}
}
# Input variables to set for your Terraform module
instance_type = "t2.micro"
instance_count = 10
With the move to terragrunt.hcl
, we no longer get this behavior for free. However, Terragrunt can simulate this
behavior for you if you define your input variables by specifying inputs = { ... }
:
# terragrunt.hcl
terraform {
# ...
}
remote_state {
# ...
}
# Input variables to set for your Terraform module
inputs = {
instance_type = "t2.micro"
instance_count = 10
}
Whenever you run a Terragrunt command, such as terragrunt apply
, Terragrunt will make these variables available to
your Terraform module as environment variables.
Terraform 0.11 only allowed special behavior, such as function calls, using “interpolation syntax,” where you wrapped
the code with ${...}
. Terragrunt included a handful of functions you could call using interpolation syntax, but
only within the terragrunt = { ... }
block:
# terraform.tfvars
terragrunt = {
terraform {
extra_arguments "retry_lock" {
# Using a function within interpolation syntax
commands = "${get_terraform_commands_that_need_locking()}"
arguments = ["-lock-timeout=20m"]
}
}
}
# Using interpolation syntax outside of the terragrunt config did NOT work before
foo = "${get_env("FOO", "default")}"
Terraform 0.12 has moved to HCL2, which has first-class support for expressions. That means you can call functions
without having to wrap them in ${...}
. Terragrunt embraces HCL2, and thanks to HCL2’s nice parser, that means we not
only get first-class expressions, but we can also use those expressions everywhere in terragrunt.hcl
!
# terragrunt.hcl
terraform {
extra_arguments "retry_lock" {
# Using a function within first-class expressions!
commands = get_terraform_commands_that_need_locking()
arguments = ["-lock-timeout=20m"]
}
}
inputs = {
# This now works with Terragrunt 0.19.x and newer!
foo = get_env("FOO", "default")
}
HCL2 is more strict about the difference between attributes:
# Attributes use an equals sign before the curly brace
foo = {
bar = "baz"
}
And blocks:
# Blocks do not use equal signs before the curly brace
foo {
bar = "baz"
}
Since Terragrunt uses HCL2, we now have to be more strict with which parts of the Terragrunt configuration are attributes and which are blocks:
# terragrunt.hcl
# terraform is a block, so make sure NOT to include an equals sign
terraform {
source = "git::git@github.com:foo/modules.git//frontend-app?ref=v0.0.3"
# extra_arguments is a block, so make sure NOT to include an equals sign
extra_arguments "custom_vars" {
commands = ["apply", "plan"]
arguments = ["-var", "foo=42"]
}
}
# remote_state is a block, so make sure NOT to include an equals sign
remote_state {
backend = "s3"
# config is an attribute, so an equals sign is REQUIRED
config = {
bucket = "foo"
# s3_bucket_tags is an attribute, so an equals sign is REQUIRED
s3_bucket_tags = {
owner = "terragrunt integration test"
name = "Terraform state storage"
}
# dynamodb_table_tags is an attribute, so an equals sign is REQUIRED
dynamodb_table_tags = {
owner = "terragrunt integration test"
name = "Terraform lock table"
}
# accesslogging_bucket_tags is an attribute, so an equals sign is REQUIRED
accesslogging_bucket_tags = {
owner = "terragrunt integration test"
name = "Terraform access log storage"
}
}
}
# include is a block, so make sure NOT to include an equals sign
include {
path = find_in_parent_folders()
}
# dependencies is a block, so make sure NOT to include an equals sign
dependencies {
paths = ["../vpc", "../mysql", "../redis"]
}
# Inputs is an attribute, so an equals sign is REQUIRED
inputs = {
instance_type = "t2.micro"
instance_count = 10
}
Two built-in functions were renamed:
get_tfvars_dir()
is now called get_terragrunt_dir()
.get_parent_tfvars_dir()
is now called get_parent_terragrunt_dir()
.Make sure to make the corresponding updates in your terragrunt.hcl
file!
Although it is not officially supported and not tested, it is still possible to use terraform<0.12 with terragrunt >=0.19.
Just install a different version of terraform into a directory of your choice outside of PATH
and specify path to the binary in terragrunt.hcl
as terraform_binary
, plus you need to lower the version check constraint:
terraform_binary = "~/bin/terraform-v11/terraform"
terraform_version_constraint = ">= 0.11"