Generate Terraform Files
The generate section creates auxiliary configuration files for Terraform components based on your stack configuration. Define file templates once in your stacks and Atmos generates the appropriate files in each component directory.
How It Works
When you run atmos terraform generate files (or any terraform command with auto_generate_files enabled), Atmos:
- Reads the
generatesection from the component's stack configuration - Processes each file definition using extension-aware serialization or Go templates
- Writes the generated files to the component directory
- Makes template context variables available for dynamic content
This separation means you can generate environment-specific configuration files without hardcoding values in your Terraform modules.
Concepts
The generate block is declarative code generation for Terraform component directories. You describe the files in stack configuration, and Atmos writes those files next to the Terraform root module before Terraform runs.
Use generate when a file belongs with a Terraform component at runtime, but the file's content should come from Atmos stack configuration. This keeps the reusable component HCL focused on infrastructure behavior while stack-specific values remain in stack manifests.
The generate section can be defined in several places:
components.terraform.<component>.generatefor one component instance- an inherited component's
generatesection, usually throughmetadata.inherits terraform.generatefor all Terraform components in the current stack scopeterraform.overrides.generatefor file-scoped Terraform component overrides with the highest priority
Files are written relative to the resolved Terraform component directory. If a component uses metadata.component, Atmos writes generated files into that resolved component folder, not necessarily a folder named after the Atmos component instance.
Add generated files to your .gitignore since they are created automatically by Atmos:
# Atmos generated files
locals.tf
*.generated.tf
*_override.tf
*_override.tf.json
Use Cases
- Dynamic Locals and Context: Generate
locals.tf,context.tf, orcontext.auto.tfvars.jsonfiles with environment-specific values from stack configuration. - Provider-Adjacent Configuration: Create extra provider aliases, required provider settings, or supporting HCL that depends on the stack context.
- Documentation: Generate README files with component metadata, workspace names, or stack-specific usage notes.
- Terraform Overrides: Generate Terraform override files to patch a vendored component without editing upstream HCL.
- Custom Configuration: Create any auxiliary files your Terraform modules need.
Terraform only treats a file as an override file when it is named override.tf, override.tf.json, *_override.tf, or *_override.tf.json.
Use those names when generating Terraform overrides. For normal generated files, use ordinary filenames like locals.tf, context.tf, or context.auto.tfvars.json.
Configuration
The generate section is defined at the component level in stack configuration:
- Stack Configuration
- Generated locals.tf
- Generated README.md
components:
terraform:
vpc:
vars:
environment: prod
vpc_cidr: "10.0.0.0/16"
generate:
# Map values are serialized based on file extension
"locals.tf":
locals:
environment: "{{ .vars.environment }}"
vpc_cidr: "{{ .vars.vpc_cidr }}"
# String values are treated as Go templates
"README.md": |
# VPC Component
Environment: {{ .vars.environment }}
Stack: {{ .atmos_stack }}
locals {
environment = "prod"
vpc_cidr = "10.0.0.0/16"
}
# VPC Component
Environment: prod
Stack: plat-ue1-prod
Extension-Aware Serialization
When the value is a map, Atmos serializes it based on the file extension:
| Extension | Format | Description |
|---|---|---|
.tf, .hcl | HCL | Valid Terraform/HCL syntax |
.json | JSON | 2-space indented JSON |
.yaml, .yml | YAML | 2-space indented YAML |
| Other | JSON | Defaults to JSON format |
HCL Generation Example
- Stack Configuration
- Generated File
components:
terraform:
vpc:
generate:
"locals.tf":
locals:
environment: "{{ .vars.environment }}"
tags:
ManagedBy: Atmos
Component: "{{ .component }}"
locals {
environment = "prod"
tags = {
Component = "vpc"
ManagedBy = "Atmos"
}
}
JSON Generation Example
- Stack Configuration
- Generated File
components:
terraform:
vpc:
generate:
"config.json":
settings:
environment: "{{ .vars.environment }}"
region: "{{ .vars.region }}"
{
"settings": {
"environment": "prod",
"region": "us-east-1"
}
}
Template Context
String values and map string values are processed as Go templates with access to the full component context:
Available Variables
{{ .vars }}- Component variables from the
varssection. {{ .settings }}- Component settings from the
settingssection. {{ .env }}- Component environment variables from the
envsection. {{ .backend }}- Backend configuration.
{{ .backend_type }}- Backend type (e.g.,
s3,azurerm). {{ .providers }}- Provider configurations.
{{ .metadata }}- Component metadata.
{{ .namespace }}- Namespace context variable.
{{ .tenant }}- Tenant context variable.
{{ .environment }}- Environment context variable.
{{ .stage }}- Stage context variable.
{{ .region }}- Region context variable (if defined in vars).
{{ .atmos_component }}- Atmos component name.
{{ .atmos_stack }}- Atmos stack name.
{{ .component }}- Final component name (after inheritance resolution).
{{ .workspace }}- Terraform workspace name.
Template Example
Auto-Generation
Enable automatic file generation during terraform commands by setting auto_generate_files in your atmos.yaml:
When enabled, Atmos generates files from the generate section before executing any terraform command (plan, apply, etc.).
Examples
Generate Locals from Variables
Create a locals.tf that mirrors select variables:
- Stack Configuration
- Generated File
components:
terraform:
eks:
vars:
cluster_name: acme-prod
cluster_version: "1.28"
node_groups:
default:
instance_type: m5.large
desired_size: 3
generate:
"locals.tf":
locals:
cluster_name: "{{ .vars.cluster_name }}"
cluster_version: "{{ .vars.cluster_version }}"
environment: "{{ .vars.environment }}"
locals {
cluster_name = "acme-prod"
cluster_version = "1.28"
environment = "prod"
}
Generate Multiple Files
Conditional Content with Templates
Use Go template conditionals for dynamic content:
components:
terraform:
vpc:
vars:
enable_nat_gateway: true
single_nat_gateway: false
generate:
"nat-config.tf": |
locals {
{{ if .vars.enable_nat_gateway }}
nat_gateway_enabled = true
{{ if .vars.single_nat_gateway }}
nat_gateway_count = 1
{{ else }}
nat_gateway_count = length(var.availability_zones)
{{ end }}
{{ else }}
nat_gateway_enabled = false
nat_gateway_count = 0
{{ end }}
}
Component Inheritance with Generate
The generate section is inherited and merged like other component sections:
- Catalog Defaults
- Stack Override
components:
terraform:
vpc/defaults:
metadata:
type: abstract
generate:
"locals.tf":
locals:
managed_by: Atmos
import:
- catalog/vpc/_defaults
components:
terraform:
vpc:
metadata:
inherits:
- vpc/defaults
generate:
# Merged with inherited generate section
"locals.tf":
locals:
managed_by: Atmos
environment: "{{ .vars.environment }}"
Terraform Overrides for Vendored Components
Use generate to create Terraform override files when you need to patch a vendored component without changing the vendored source. This is useful for small, deliberate changes such as overriding an argument, adding a lifecycle rule, or adding a provider alias while keeping upstream code intact.
- Stack Configuration
- Generated Override
components:
terraform:
vpc:
metadata:
component: cloudposse/vpc
vars:
protect_default_security_group: true
generate:
"default_security_group_override.tf": |
# Generated by Atmos - DO NOT EDIT
resource "aws_default_security_group" "default" {
lifecycle {
prevent_destroy = {{ .vars.protect_default_security_group }}
}
}
# Generated by Atmos - DO NOT EDIT
resource "aws_default_security_group" "default" {
lifecycle {
prevent_destroy = true
}
}
Terraform loads this file as an override because the filename ends in _override.tf. Use this pattern carefully: Terraform overrides depend on resource names and block structure inside the upstream component, so they can break when the vendored component changes.
File-Scoped Generate Overrides
Use terraform.overrides.generate when the generated file should apply only to the Terraform components visible from the current manifest and its imports. Like other overrides, this is file-scoped and has the highest merge priority.
import:
- catalog/vpc/defaults
- catalog/transit-gateway/defaults
terraform:
overrides:
generate:
"team-context.generated.tf":
locals:
team: Network
managed_by: Atmos
Cleaning Generated Files
Remove generated files with the --clean flag:
# Clean generated files for a single component
atmos terraform generate files vpc -s prod-ue2 --clean
# Clean generated files for all components
atmos terraform generate files --all --clean
Best Practices
-
Use for Environment-Specific Values: Generate files for values that vary by environment rather than hardcoding in modules.
-
Add to .gitignore: Generated files should not be committed to version control.
-
Document Generated Files: Add comments indicating the file is generated and should not be edited manually.
-
Use Consistent Naming: Establish naming conventions for generated files (e.g.,
*.generated.tf). -
Leverage Inheritance: Define common generate patterns in catalog defaults and inherit them.
-
Preview Before Applying: Use
--dry-runto preview what will be generated.
Related
- atmos terraform generate files - CLI command reference
- Terraform Components - Component configuration overview
- Terraform Backend - Backend file generation
- Terraform Providers - Provider file generation
- Variables (vars) - Defining component variables