Skip to content

Instantly share code, notes, and snippets.

@pap-12
Last active June 23, 2026 07:37
Show Gist options
  • Select an option

  • Save pap-12/2ff107db02255b489d5b60ed3da7443a to your computer and use it in GitHub Desktop.

Select an option

Save pap-12/2ff107db02255b489d5b60ed3da7443a to your computer and use it in GitHub Desktop.
kiro-steering-terragrunt-conventions

Terragrunt Conventions

Terragrunt is the deployment orchestrator. It handles remote state, provider generation, and multi-account role assumption.

Agent Instructions

  • Always use terragrunt CLI commands — never call tofu directly for deployment
  • When running commands across all modules: terragrunt run-all <command>
  • When targeting a single module: run terragrunt <command> from within its live/ directory
  • Terragrunt version managed by mise (terragrunt = "1.0.0")

Config Hierarchy

  • project.hcl — single source of truth for org-wide values (project name, account IDs, emails, org/OU IDs)
  • root.hcl — remote state config (S3 bucket naming: {project}-{env}-terraform-state) and provider generation with role assumption (terragrunt-execution-role)
  • account.hcl — account name and AWS account ID only
  • env.hcl — all module configuration values for a given account+region scope; includes skip_module map and tags block

Module Deployment Pattern

Each module deployment is a thin terragrunt.hcl that:

  1. Sources either a local module (../../../../modules/{name}) or external git module with pinned ref
  2. Includes root.hcl (for backend/provider)
  3. Includes env.hcl with expose = true and merge_strategy = "no_merge"
  4. Passes inputs from include.env.locals.*
  5. Uses exclude block with include.env.locals.skip_module.{name} for conditional deployment

When Writing terragrunt.hcl Files

  1. Always include root.hcl for backend/provider
  2. Include env.hcl with expose = true and merge_strategy = "no_merge"
  3. Pass inputs from include.env.locals.*
  4. Add exclude block referencing skip_module map
  5. Source local modules as ../../../../modules/{name} or external with pinned ref

Skip Module Pattern

Each env.hcl defines a skip_module map with boolean values per module. Set to true to exclude a module from deployment without removing its directory:

skip_module = {
  vpc = false
  s3  = true   # skipped
}

External Module References

Pin external modules with exact git refs (no latest, no branch names):

terraform {
  source = "git::https://github.com/terraform-aws-modules/terraform-aws-vpc.git?ref=v6.0.1"
}

Naming Conventions

  • S3 state buckets: {project}-{account}-terraform-state
  • Variable names in env.hcl: snake_case, prefixed by module context (vpc_cidr, budget_limit_amount)

State Locking

This project uses S3 native state locking (use_lockfile = true) instead of DynamoDB. This requires OpenTofu >= 1.10 (we use 1.11.2). S3 creates a .tflock file next to the state file using conditional writes — no DynamoDB table needed.

  • Do NOT use dynamodb_table in backend config — it is deprecated
  • Do NOT create DynamoDB lock tables for new accounts
  • If migrating an existing account from DynamoDB locking, remove the dynamodb_table argument and add use_lockfile = true

Tagging Strategy

All resources must be tagged. Tags are defined once in env.hcl and passed to modules via inputs.

Required Tags

Every resource must include these tags (no exceptions):

Tag Value Description
AccountType platform or workload Distinguishes shared infra accounts from workload accounts
CreatedBy terragrunt Always this value for IaC-managed resources
Environment {account_name} Matches the account name (e.g., production, development)
Owner {project} Project name from project.hcl
Project {project} Project name from project.hcl
Version {project_version} Semantic version from project.hcl

Tag Block Template (in env.hcl)

tags = {
  AccountType = "platform"       # or "workload"
  CreatedBy   = "terragrunt"
  Environment = "${local.env}"
  Owner       = "${local.project}"
  Project     = "${local.project}"
  Version     = "${local.project_version}"
}

Agent Instructions for Tagging

  • When creating a new env.hcl, always include the full tags block — copy from existing env files
  • When creating a new module, accept a tags variable of type map(string) and apply it to all resources
  • Never hardcode tag values inside modules — they must come from env.hcl via inputs
  • AccountType must be "platform" for shared accounts (management, security, shared-services, monitoring) and "workload" for application accounts (development, production, sandbox)
  • Never add tags that contain sensitive info (account IDs, secrets, internal URLs)
  • If a resource supports tags_all (e.g., AWS provider default tags), prefer passing tags via the provider; otherwise pass explicitly per resource

Module Variable Pattern

Every module that creates taggable resources must include:

variable "tags" {
  description = "Tags to apply to all resources"
  type        = map(string)
  default     = {}
}

And apply in resources:

resource "aws_whatever" "this" {
  # ...
  tags = var.tags
}

Common Commands

# Plan all modules in an account/region
terragrunt run-all plan

# Apply a single module
terragrunt apply    # (from within live/{account}/{region}/{module}/)

# Format all HCL
terragrunt run-all fmt

# Validate all modules
terragrunt run-all validate

Versioning

  • Semantic versioning via semantic-release
  • Conventional commits required (enforced by commitizen)
  • Version tracked in project.hcl as project_version
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment