Generating DRY Terraform Configurations with Terramate Code Generation
Use Terramate globals and generate_hcl to define provider and backend configuration once, then generate consistent Terraform files into every stack — eliminating duplication while keeping stacks independently runnable.
03 Jul 2026, 03:16 UTC

Eliminating Boilerplate Across Terraform Stacks
Teams managing dozens of Terraform stacks often duplicate provider configurations, backend blocks, and common tags across every stack directory. Terramate's code generation feature solves this by letting you define shared configuration once in a globals block and generate the required files into each stack at runtime. The useful takeaway: you maintain a single source of truth for repeated Terraform constructs while keeping each stack independently executable by Terraform.
Prerequisites
- Terramate CLI installed (v0.8+ for stable
generate_hclsyntax). - A repository structured with a root
terramate.tm.hcland one or more stack directories (each containing astack.tm.hcl). - Terraform >= 1.0 available in PATH for subsequent plan/apply steps.
Defining Shared Configuration in Globals
At the repository root, create or edit terramate.tm.hcl to declare globals that every stack can reference. Globals are evaluated before code generation runs.
# terramate.tm.hcl
globals {
aws_region = "us-east-1"
common_tags = {
managed_by = "terramate"
environment = "production"
cost_center = "engineering"
}
s3_backend = {
bucket = "my-org-terraform-state"
key_prefix = "stacks"
dynamodb_table = "terraform-locks"
encrypt = true
}
required_providers = {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
random = {
source = "hashicorp/random"
version = "~> 3.0"
}
}
}Generating Provider and Backend Blocks per Stack
In each stack's stack.tm.hcl, add a generate_hcl block that writes provider.tf and backend.tf using the globals. The filename is relative to the stack directory.
# stacks/vpc/stack.tm.hcl
stack {
name = "vpc"
description = "Core VPC and networking"
}
generate_hcl "provider" {
filename = "provider.tf"
content = jsonencode({
terraform = {
required_providers = globals.required_providers
required_version = ">= 1.0"
}
provider = {
aws = {
region = globals.aws_region
default_tags = [{
tags = globals.common_tags
}]
}
}
})
}
generate_hcl "backend" {
filename = "backend.tf"
content = jsonencode({
terraform = {
backend = {
s3 = {
bucket = globals.s3_backend.bucket
key = "${globals.s3_backend.key_prefix}/${terramate.stack.name}/terraform.tfstate"
region = globals.aws_region
dynamodb_table = globals.s3_backend.dynamodb_table
encrypt = globals.s3_backend.encrypt
}
}
}
})
}Running Generation and Verifying Output
Execute generation from the repository root. Terramate writes the generated files into each stack directory.
# Run from repository root
terramate generateAfter generation, inspect a stack to confirm the files exist and contain the expected values:
cat stacks/vpc/provider.tf
cat stacks/vpc/backend.tfYou should see valid HCL (Terramate pretty-prints the JSON-encoded content). Each stack now has its own provider.tf and backend.tf with the correct bucket key path derived from terramate.stack.name.
Integrating with Terraform Workflows
Generated files are committed to version control (recommended) or added to .gitignore and regenerated in CI. For CI pipelines, run terramate generate before terramate run terraform plan. The terramate run command executes Terraform only in stacks that have changed (change detection), using the generated configuration.
# Example CI step
terramate generate
terramate run --change-detection=git terraform planLimitations and Checks
- Generated files overwrite existing files with the same name. Keep hand-written Terraform in separate files (e.g.,
main.tf,variables.tf). - Globals cannot reference stack-specific attributes (like
terramate.stack.name) — only thegenerate_hclblock has access to stack context. - If you change globals, re-run
terramate generateacross all stacks. There is no automatic drift detection for generated files. - Version note:
generate_hclwithjsonencodeis stable in Terramate >= 0.8. Earlier versions usedgenerate_filewith template syntax; migration is manual.
Practical Verification
After generating, run terraform init inside a stack directory to confirm the backend initializes correctly and the provider block is valid. A successful init without errors confirms the generated HCL is syntactically correct and the backend configuration resolves.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.