Skip to main content

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.

Experimental

How It Works

When you run atmos terraform generate files (or any terraform command with auto_generate_files enabled), Atmos:

  1. Reads the generate section from the component's stack configuration
  2. Processes each file definition using extension-aware serialization or Go templates
  3. Writes the generated files to the component directory
  4. 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>.generate for one component instance
  • an inherited component's generate section, usually through metadata.inherits
  • terraform.generate for all Terraform components in the current stack scope
  • terraform.overrides.generate for 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.

Generated Files

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, or context.auto.tfvars.json files 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 Override Filenames

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:

stacks/orgs/acme/plat/prod/us-east-1.yaml
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 }}

Extension-Aware Serialization

When the value is a map, Atmos serializes it based on the file extension:

ExtensionFormatDescription
.tf, .hclHCLValid Terraform/HCL syntax
.jsonJSON2-space indented JSON
.yaml, .ymlYAML2-space indented YAML
OtherJSONDefaults to JSON format

HCL Generation Example

components:
terraform:
vpc:
generate:
"locals.tf":
locals:
environment: "{{ .vars.environment }}"
tags:
ManagedBy: Atmos
Component: "{{ .component }}"

JSON Generation Example

components:
terraform:
vpc:
generate:
"config.json":
settings:
environment: "{{ .vars.environment }}"
region: "{{ .vars.region }}"

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 vars section.
{{ .settings }}
Component settings from the settings section.
{{ .env }}
Component environment variables from the env section.
{{ .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

stacks/orgs/acme/plat/prod/us-east-1.yaml
components:
terraform:
vpc:
vars:
environment: prod
vpc_cidr: "10.0.0.0/16"
availability_zones:
- us-east-1a
- us-east-1b
generate:
"context.tf": |
# Generated by Atmos - DO NOT EDIT
locals {
atmos_stack = "{{ .atmos_stack }}"
atmos_component = "{{ .atmos_component }}"
environment = "{{ .vars.environment }}"
}

Auto-Generation

Enable automatic file generation during terraform commands by setting auto_generate_files in your atmos.yaml:

atmos.yaml
components:
terraform:
auto_generate_files: true

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:

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 }}"

Generate Multiple Files

stacks/catalog/vpc/defaults.yaml
components:
terraform:
vpc:
generate:
# HCL locals file
"locals.tf":
locals:
environment: "{{ .vars.environment }}"
stage: "{{ .stage }}"

# JSON configuration
"atmos-metadata.json":
stack: "{{ .atmos_stack }}"
component: "{{ .atmos_component }}"
workspace: "{{ .workspace }}"

# Markdown documentation
"README.md": |
# {{ .atmos_component }}

This component is deployed to the `{{ .atmos_stack }}` stack.

## Variables

- Environment: {{ .vars.environment }}
- Stage: {{ .stage }}

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:

stacks/catalog/vpc/_defaults.yaml
components:
terraform:
vpc/defaults:
metadata:
type: abstract
generate:
"locals.tf":
locals:
managed_by: Atmos

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.

stacks/catalog/vpc/defaults.yaml
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 }}
}
}

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.

stacks/teams/network.yaml
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

  1. Use for Environment-Specific Values: Generate files for values that vary by environment rather than hardcoding in modules.

  2. Add to .gitignore: Generated files should not be committed to version control.

  3. Document Generated Files: Add comments indicating the file is generated and should not be edited manually.

  4. Use Consistent Naming: Establish naming conventions for generated files (e.g., *.generated.tf).

  5. Leverage Inheritance: Define common generate patterns in catalog defaults and inherit them.

  6. Preview Before Applying: Use --dry-run to preview what will be generated.