At CWISE we use Terraform in conjunction with Terramate (https://terramate.io) very extensively
Terramate is being used to do the following things:
Scale and orchestrate large Terraform workflows, to avoid Terralith
Auto generate Terraform code
There are alternative solutions like Terragrunt. But based on our experience with TM since 2022 we have found that it's much more easier and faster to use than Terragrunt.
One of the main features of Terramate is code generation. For example, generating common backend or data source configurations everywhere in the code base provides consistency.
The Terramate code generation feature is very straightforward:
You define a code block to generate with the "generate_hcl" function. For example, backend generation for all code base from global terramate.tm.hcl configuration:
generate_hcl "backend.tf" {
content {
terraform {
required_version = "1.5.1"
backend "s3" {
bucket = "terraform-state-example"
key = "tf${terramate.path}"
region = "eu-north-1"
dynamodb_table = "terraform-state-example"
encrypt = true
}
}
}
}
Execute "terramate create" or "terramate generate" and code will be generated everywhere as "backend.tf"
Terramate Conditional Generation using tm_try
There are cases, when basic Terramate general code generation falls short. For example, scenarios where you need to create Terraform provider configuration with different providers.
To tackle these cases, we use tm_try function, to generate code conditionally.
How does it work? tm_try to find the first true condition. In most cases, we are using boolean.
See full example repo: https://github.com/cloudwise-devops/terraform-terramate-tm-try-example
For example, in main terramate.tm.cl we set conditional up data source generation:
terramate {
config {
git {
default_branch = "main"
}
}
}
globals {
generate_data_source_google_ip = true
generate_data_source_facebook_ip = false
}
generate_hcl "terramate_data.tf" {
condition = tm_try(global.generate_data_source_google_ip, true)
content {
data "dns_a_record_set" "domain" {
host = "google.com"
}
}
}
generate_hcl "terramate_data.tf" {
condition = tm_try(global.generate_data_source_facebook_ip, true)
content {
data "dns_a_record_set" "domain" {
host = "facebook.com"
}
}
}
And in Terramate stack level in for where to obtain the Facebook dns record we will set in the globals:
globals {
generate_data_source_google_ip = false
generate_data_source_facebook_ip = true
}
This will ensure that the desired outcome is generated.
Update from Terramate Team to use tm_dynamic
Soren from Terramate suggested to use tm_dynamic
globals {
generate_data_source_google_ip = true
}
generate_hcl "terramate_data.tf" {
lets {
condition = tm_try(global.generate_data_source_google_ip, true)
}
content {
tm_dynamic "resource" {
for_each = tm_toset(["domain"])
iterator = value
labels = ["dns_a_record_set", value.value]
content {
host = tm_ternary(true, "google.com", "facebook.com")
}
}
}
}
Limitations of tm_try
Main limitation of tm_try is that you need to define two separate conditions:
generate_hcl "terramate_data.tf" {
condition = tm_try(global.generate_data_source_google_ip, true)
content {
data "dns_a_record_set" "domain" {
host = "google.com"
}
}
}
generate_hcl "terramate_data.tf" {
condition = tm_try(global.generate_data_source_google_ip, false)
content {
data "dns_a_record_set" "domain" {
host = "facebook.com"
}
}
}
Will not work and will generate the error of code generation
Summary
Terramate with tm_try makes code generation much more flexible for Terraform, allowing you to scale workflows.
There are some limitations, but in general it's very useful tool