Skip to content

YAML Anchors for Configuration Reuse

YAML anchors enable DRY (Don’t Repeat Yourself) principles in your Terrateam configuration by defining reusable templates. This reduces configuration size, ensures consistency, and makes updates easier.

Use YAML anchors when you have:

  • Multiple workflows with similar configurations
  • Repeated engine settings across environments
  • Common workflow step sequences
  • Standardized access control patterns
  • Shared directory configurations

Define anchors in the definitions section and reference them throughout your configuration:

definitions:
my_config: &my_config
key: value
workflows:
- name: default
custom: *my_config

Define once, use everywhere:

definitions:
standard_engine: &standard_engine
version: 1.6.0
environment:
TF_IN_AUTOMATION: "true"
TF_INPUT: "false"
workflows:
- name: development
tag_query: "dev"
engine: *standard_engine
- name: production
tag_query: "prod"
engine:
<<: *standard_engine
environment:
<<: *standard_engine.environment
PRODUCTION: "true"

Anchor each step individually so they can be combined into a flat list:

definitions:
# Anchor individual steps, not the surrounding list
step_init: &step_init
type: init
step_fmt: &step_fmt
type: run
cmd: ["terraform", "fmt", "-check"]
step_validate: &step_validate
type: run
cmd: ["terraform", "validate"]
step_checkov: &step_checkov
type: checkov
step_tfsec: &step_tfsec
type: run
cmd: ["tfsec", "."]
workflows:
- name: default
plan:
- *step_init
- *step_fmt
- *step_validate
- *step_checkov
- *step_tfsec
- type: plan
- type: cost_estimation

If you want to reuse a complete step list, anchor it and reference it as the entire value (not as one entry in a larger list):

definitions:
full_plan: &full_plan
- type: init
- type: plan
- type: checkov
workflows:
- name: default
plan: *full_plan

Manage multiple environments efficiently:

definitions:
# AWS environment configurations
aws_dev: &aws_dev
AWS_REGION: us-east-1
AWS_ROLE_ARN: arn:aws:iam::123456789012:role/terrateam-dev
ENVIRONMENT: development
aws_staging: &aws_staging
AWS_REGION: us-east-1
AWS_ROLE_ARN: arn:aws:iam::123456789012:role/terrateam-staging
ENVIRONMENT: staging
aws_prod: &aws_prod
AWS_REGION: us-east-1
AWS_ROLE_ARN: arn:aws:iam::123456789012:role/terrateam-prod
ENVIRONMENT: production
# Standard requirements
basic_requirements: &basic_requirements
- approved
- status_checks
strict_requirements: &strict_requirements
- approved: 2
- status_checks
- merge_conflicts
workflows:
- name: development
tag_query: "dev"
engine:
version: 1.6.0
environment: *aws_dev
apply_requirements: *basic_requirements
- name: staging
tag_query: "staging"
engine:
version: 1.6.0
environment: *aws_staging
apply_requirements: *basic_requirements
- name: production
tag_query: "production"
engine:
version: 1.6.0
environment: *aws_prod
apply_requirements: *strict_requirements

Define access patterns once:

definitions:
dev_team_access: &dev_team_access
plan: ["*"]
apply: ["team:developers", "team:platform"]
platform_only: &platform_only
plan: ["*"]
apply: ["team:platform"]
apply_force: ["team:sre"]
access_control:
enabled: true
policies:
- tag_query: "dev or staging"
<<: *dev_team_access
- tag_query: "production"
<<: *platform_only
- tag_query: "infrastructure"
<<: *platform_only

Build sophisticated reusable patterns:

Anchor individual steps so you can compose them into different workflow lists without producing nested arrays:

definitions:
# Base configuration for all workflows
base_config: &base_config
engine:
version: 1.6.0
tf_version: 1.5.0
# Individual step anchors (compose these into flat lists)
step_init: &step_init
type: init
step_fmt: &step_fmt
type: run
cmd: ["terraform", "fmt", "-check"]
step_validate: &step_validate
type: run
cmd: ["terraform", "validate"]
step_plan: &step_plan
type: plan
step_checkov: &step_checkov
type: checkov
when: always
step_tfsec: &step_tfsec
type: run
cmd: ["tfsec", ".", "--format", "json"]
when: always
step_cost: &step_cost
type: cost_estimation
when: always
step_notify: &step_notify
type: run
cmd: ["echo", "Deployment complete"]
when: apply_succeeded
workflows:
- name: feature-branch
<<: *base_config
tag_query: "feature"
plan:
- *step_init
- *step_fmt
- *step_validate
- *step_plan
- *step_checkov
- *step_tfsec
- *step_cost
- name: main-branch
<<: *base_config
tag_query: "main"
plan:
- *step_init
- *step_fmt
- *step_validate
- *step_plan
- *step_checkov
- *step_tfsec
- *step_cost
apply:
- type: init
- type: apply
- *step_notify

Share configurations across directories:

definitions:
# S3 backend configuration
s3_backend: &s3_backend
backend: s3
backend_config:
bucket: terraform-state
region: us-east-1
encrypt: true
# Anchor individual tag values so they can be composed into flat lists
tag_dir: &tag_dir "$dir"
tag_workspace: &tag_workspace "$workspace"
# Module directories configuration
module_config: &module_config
autoplan: false
tags:
- *tag_dir
- *tag_workspace
- "module"
dirs:
- path: terraform/networking
<<: *s3_backend
tags:
- *tag_dir
- *tag_workspace
- "networking"
- "core"
- path: terraform/compute
<<: *s3_backend
tags:
- *tag_dir
- *tag_workspace
- "compute"
- "application"
- path: modules/**
<<: *module_config

Combine configurations using the merge operator:

definitions:
base: &base
version: 1.6.0
aws: &aws
environment:
AWS_REGION: us-east-1
monitoring: &monitoring
environment:
DATADOG_API_KEY: ${DATADOG_API_KEY}
complete: &complete
<<: *base
<<: *aws
<<: *monitoring
environment:
CUSTOM: value
workflows:
- name: monitored
engine: *complete

Anchor each step individually and assemble per-environment lists from those steps. YAML cannot merge two lists into one, so the production list has to repeat the base entries — but each step is still defined exactly once.

definitions:
# Anchor individual steps
step_init: &step_init
type: init
step_plan: &step_plan
type: plan
step_checkov: &step_checkov
type: checkov
step_cost: &step_cost
type: cost_estimation
step_compliance: &step_compliance
type: run
cmd: ["compliance-check"]
# Development workflow - basic
dev_plan: &dev_plan
- *step_init
- *step_plan
# Production workflow - full compliance
prod_plan: &prod_plan
- *step_init
- *step_plan
- *step_checkov
- *step_cost
- *step_compliance
workflows:
- name: development
tag_query: "dev"
plan: *dev_plan
- name: production
tag_query: "production"
plan: *prod_plan
  1. Name anchors descriptively - Use clear names that indicate purpose
  2. Group related anchors - Keep similar configurations together
  3. Document complex anchors - Add comments explaining usage
  4. Start simple - Begin with basic patterns and evolve as needed
  5. Test thoroughly - Verify anchors resolve correctly before deploying
definitions:
# Terraform versions
tf_latest: &tf_latest
version: 1.6.0
tf_version: 1.5.0
# Individual step anchors (compose into flat lists below)
step_init: &step_init
type: init
step_fmt: &step_fmt
type: run
cmd: ["terraform", "fmt", "-check"]
step_validate: &step_validate
type: run
cmd: ["terraform", "validate"]
step_plan: &step_plan
type: plan
step_checkov: &step_checkov
type: checkov
step_cost: &step_cost
type: cost_estimation
# Environment credentials
dev_env: &dev_env
AWS_ROLE_ARN: arn:aws:iam::111111111111:role/terrateam-dev
AWS_REGION: us-east-1
prod_env: &prod_env
AWS_ROLE_ARN: arn:aws:iam::222222222222:role/terrateam-prod
AWS_REGION: us-east-1
# Access patterns
dev_access: &dev_access
plan: ["*"]
apply: ["team:developers"]
prod_access: &prod_access
plan: ["*"]
apply: ["team:platform"]
apply_force: ["team:sre"]
# Apply configurations
workflows:
- name: development
<<: *tf_latest
tag_query: "dev"
engine:
<<: *tf_latest
environment: *dev_env
plan:
- *step_init
- *step_fmt
- *step_validate
- *step_plan
- *step_checkov
- *step_cost
apply_requirements:
- approved
- name: production
<<: *tf_latest
tag_query: "production"
engine:
<<: *tf_latest
environment: *prod_env
plan:
- *step_init
- *step_fmt
- *step_validate
- *step_plan
- *step_checkov
- *step_cost
apply_requirements:
- approved: 2
- status_checks
access_control:
enabled: true
policies:
- tag_query: "dev"
<<: *dev_access
- tag_query: "production"
<<: *prod_access

This approach reduces a 200+ line configuration to under 100 lines while improving maintainability and consistency.