Terragrunt Generator
Comprehensive toolkit for generating best-practice Terragrunt configurations (HCL files) following current standards and conventions. Generates terragrunt.hcl files, root configurations, child modules, stacks, and environment setups; configures remote state backends, dependency blocks, include blocks, feature flags, exclude blocks, and errors blocks; supports DRY Terraform patterns, multi-environment layouts (dev/staging/prod), and OpenTofu engine integration. Use when creating new Terragrunt projects or resources, scaffolding multi-environment infrastructure, implementing DRY Terraform wrapper configurations, setting up terragrunt.hcl files with remote state or provider config, managing module dependencies, or building infrastructure modules with Terragrunt stacks.
npx skills add github:pantheon-org/tekhne --skill infrastructure--terragrunt--generatorPhilosophy
- Choose the architecture pattern (A, B or C) before writing any file, because every later path depends on it.
- Keep
root.hclenvironment-agnostic unless the pattern explicitly says otherwise; child modules readenv.hcl. - Define shared configuration once in the root and inherit it, rather than copying it into each unit.
- Generate from the bundled templates and
references/common-patterns.mdinstead of writing HCL from memory. - A configuration is not finished until it has passed validation with
terragrunt-validator.
When to Use
- Creating a new
root.hcl, child module, standalone module, stack or catalog unit. - Scaffolding a multi-environment layout (dev, staging, prod) with shared remote state.
- Configuring dependency blocks with mock outputs, feature flags, exclude blocks or errors blocks.
- Setting up remote state and provider configuration through
generateblocks. - Migrating generated configuration to current syntax such as
run --all,excludeanderrors.
When Not to Use
- Validating or debugging existing Terragrunt files: use the
terragrunt-validatorskill instead. - Writing plain Terraform modules with no Terragrunt wrapper: use
terraform-generator. - Applying or destroying infrastructure, which this skill does not do.
- Producing partial snippets for documentation, where full generation and validation add no value.
Overview
Generate production-ready Terragrunt configurations following current best practices, naming conventions, and security standards. All generated configurations are automatically validated.
Terragrunt 2025 Features Supported:
- Stacks - Infrastructure blueprints with
terragrunt.stack.hcl(GA since v0.78.0) - Feature Flags - Runtime control via
featureblocks - Exclude Blocks - Fine-grained execution control (replaces deprecated
skip) - Errors Blocks - Advanced error handling (replaces deprecated
retryable_errors) - OpenTofu Engine - Alternative IaC engine support
Root Configuration Naming
RECOMMENDED: Use
root.hclinstead ofterragrunt.hclfor root files per migration guide.
| Approach | Root File | Include Syntax |
|---|---|---|
| Modern | root.hcl |
find_in_parent_folders("root.hcl") |
| Legacy | terragrunt.hcl |
find_in_parent_folders() |
Architecture Patterns
CRITICAL: Before generating ANY configuration, determine the architecture pattern and understand its constraints.
Pattern Selection
| Pattern | Use When | Root Behavior | Structure |
|---|---|---|---|
| A: Multi-Env Agnostic | Multiple environments with shared root | Root reads NO env files; uses static values or get_env() |
root.hcl + {env}/env.hcl per environment |
| B: Single/Env-Aware | Single environment OR environment detection needed | Root can parse path or read get_env() |
root.hcl with optional account.hcl/region.hcl |
| C: Centralized Vars | Shared environment definitions | Root is agnostic; env.hcl reads from _env/ |
root.hcl + _env/{env}.hcl + {env}/env.hcl |
Pattern A: Multi-Environment Agnostic Root
Key principle: root.hcl does NOT read env.hcl. Child modules read env.hcl directly.
infrastructure/
├── root.hcl # Environment-AGNOSTIC
├── dev/env.hcl # locals { environment = "dev" }
│ └── vpc/terragrunt.hcl
└── prod/env.hcl # locals { environment = "prod" }
└── vpc/terragrunt.hcl
Child module pattern:
include "root" { path = find_in_parent_folders("root.hcl") }
locals { env = read_terragrunt_config(find_in_parent_folders("env.hcl")) }
inputs = { name = "${local.env.locals.environment}-vpc" }
Pattern B: Environment-Aware Root
Key principle: Root detects environment from path or environment variable.
# root.hcl
locals {
path_parts = split("/", path_relative_to_include())
environment = local.path_parts[0] # OR: get_env("TG_ENVIRONMENT", "dev")
}
Pattern C: Centralized Variables
Key principle: Each env.hcl reads from centralized _env/{env}.hcl.
# prod/env.hcl
locals {
env_vars = read_terragrunt_config("${get_repo_root()}/_env/prod.hcl")
environment = local.env_vars.locals.environment
aws_region = local.env_vars.locals.aws_region
}
Decision: Multi-env + shared root → A | Single env / env detection → B | Centralized vars → C
Core Capabilities
1. Generate Root Configuration
Create root-level root.hcl or terragrunt.hcl with remote state, provider config, and common variables.
Read before generating: assets/templates/root/terragrunt.hcl
Patterns: references/common-patterns.md → Root Configuration Patterns
Key placeholders to replace:
[BUCKET_NAME],[AWS_REGION],[DYNAMODB_TABLE][TERRAFORM_VERSION],[PROVIDER_NAME],[PROVIDER_VERSION][ENVIRONMENT],[PROJECT_NAME]
Root.hcl Design Principles:
- Environment-agnostic by default — Don’t assume env.hcl exists at root level
- Use static values for provider/backend region — Or
get_env()for runtime config - State key uses
path_relative_to_include()— Automatically includes environment path - Provider tags can be static — Environment-specific tags go in child modules
2. Generate Child Module Configuration
Create child modules with dependencies, mock outputs, and proper includes.
Read before generating: assets/templates/child/terragrunt.hcl
Patterns: references/common-patterns.md → Child Module Patterns
Module source options:
- Local: a relative path to the modules directory, for example
"${get_repo_root()}/modules/vpc" - Git:
"git::https://github.com/org/repo.git//path?ref=v1.0.0" - Registry:
"tfr:///terraform-aws-modules/vpc/aws?version=5.1.0"
3. Generate Standalone Module
Self-contained modules without root dependency.
Read before generating: assets/templates/module/terragrunt.hcl
4. Generate Multi-Environment Infrastructure
Complete directory structures for dev/staging/prod.
Before generating:
- Determine architecture pattern (see Architecture Patterns section)
- Read relevant templates for root and child modules
- Verify env.hcl placement and access patterns
Patterns: references/common-patterns.md → Environment-Specific Patterns
Typical structure (Pattern A):
infrastructure/
├── root.hcl # Environment-AGNOSTIC root config
├── dev/
│ ├── env.hcl # Dev environment variables
│ └── vpc/terragrunt.hcl
└── prod/
├── env.hcl # Prod environment variables
└── vpc/terragrunt.hcl
5. Generate Terragrunt Stacks (2025)
Infrastructure blueprints using terragrunt.stack.hcl.
Read before generating: assets/templates/stack/terragrunt.stack.hcl and assets/templates/catalog/terragrunt.hcl
Docs: Stacks Documentation
Patterns: references/common-patterns.md → Stacks Patterns
Commands:
terragrunt stack generate # Generate unit configurations
terragrunt stack run plan # Plan all units
terragrunt stack run apply # Apply all units
terragrunt stack output # Get aggregated outputs
terragrunt stack clean # Clean generated directories
6. Generate Feature Flags (2025)
Runtime control without code changes.
Docs: Feature Flags Documentation
Patterns: references/common-patterns.md → Feature Flags Patterns
CRITICAL: Feature flag
defaultvalues MUST be static (boolean, string, number) — they CANNOT referencelocal.*values.
# Correct: static default
feature "enable_monitoring" {
default = false
}
# Incorrect: dynamic reference — FAILS
feature "enable_monitoring" {
default = local.env.locals.enable_monitoring
}
Usage:
terragrunt apply --feature enable_monitoring=true
# or
export TG_FEATURE="enable_monitoring=true"
7. Generate Exclude Blocks (2025)
Fine-grained execution control (replaces deprecated skip).
Docs: Exclude Block Reference
Patterns: references/common-patterns.md → Exclude Block Patterns
Actions: "plan", "apply", "destroy", "all", "all_except_output"
Production recommendation: Protect critical resources from accidental destruction:
exclude {
if = true
actions = ["destroy"]
exclude_dependencies = false
}
prevent_destroy = true
8. Generate Errors Blocks (2025)
Advanced error handling (replaces deprecated retryable_errors).
Docs: Errors Block Reference
Patterns: references/common-patterns.md → Errors Block Patterns
9. Generate OpenTofu Engine Configuration (2025)
Use OpenTofu as the IaC engine.
Docs: Engine Documentation
Patterns: references/common-patterns.md → OpenTofu Engine Patterns
10. Handling Custom Providers/Modules
When generating configs with custom providers:
- Identify the provider name, source, and version
- Search using WebSearch:
"[provider] terraform provider [version] documentation" - Or use Context7 MCP if available for structured docs
- Generate with proper
required_providersblock - Document authentication requirements in comments
Generation Workflow
CRITICAL: Follow this workflow for EVERY generation task. Skipping steps leads to validation errors.
Step 1: Understand Requirements
- What type of configuration? (root, child, standalone, stack)
- Single or multi-environment?
- What dependencies exist between modules?
- What providers/modules will be used?
Step 2: Determine Architecture Pattern and Complete Checklist
| Scenario | Pattern | Root.hcl Scope |
|---|---|---|
| Multi-env with shared root | Pattern A | Environment-agnostic |
| Single environment | Pattern B | Environment-aware |
| Centralized env vars | Pattern C | Environment-agnostic |
MANDATORY: Before writing any files, complete and output this checklist to the user.
## Architecture Pattern Selection
[x] Identified architecture pattern: Pattern ___ (A/B/C)
[x] Root.hcl scope: [ ] environment-agnostic OR [ ] environment-aware
[x] env.hcl location: ___________________
[x] Child modules access env via: ___________________
[x] Verified: No file references a path that doesn't exist from its location
Step 3: Read Required Templates
| Configuration Type | Template to Read |
|---|---|
| Root configuration | assets/templates/root/terragrunt.hcl |
| Child module | assets/templates/child/terragrunt.hcl |
| Standalone module | assets/templates/module/terragrunt.hcl |
| Stack file | assets/templates/stack/terragrunt.stack.hcl |
| Catalog unit | assets/templates/catalog/terragrunt.hcl |
Also read: references/common-patterns.md — primary source for all generation patterns.
Step 4: Generate with Validation
Generation order for multi-environment projects:
-
Generate root.hcl first
- No
read_terragrunt_config(find_in_parent_folders("env.hcl"))if environment-agnostic -
remote_stateblock hasencrypt = true -
errorsblock used (not deprecatedretryable_errors)
- No
-
Generate env.hcl files for each environment
-
localsblock contains environment, aws_region, and module-specific vars - No references to files that don’t exist at that directory level
-
-
Generate child modules — modules with NO dependencies first
-
includeblock usesfind_in_parent_folders("root.hcl") -
read_terragrunt_config(find_in_parent_folders("env.hcl"))present -
terraform.sourceuses valid syntax (tfr:///, git::, or relative path)
-
-
Generate dependent modules (RDS, EKS, etc.)
-
dependencyblocks havemock_outputs -
mock_outputs_allowed_terraform_commandsincludes["validate", "plan", "destroy"] - Production modules have
prevent_destroy = trueand/orexcludeblock
-
-
Run batch validation after ALL files are generated:
terragrunt hcl fmt --check # Format validation terragrunt dag graph # Dependency graph validationInvoke the
terragrunt-validatorskill for comprehensive validation.
Step 5: Fix and Re-Validate
If validation fails:
- Analyze errors (path resolution, missing variables, syntax errors)
- Fix issues in the specific file(s)
- Re-validate the fixed file(s)
- Repeat until ALL errors are resolved
Step 6: Present Results
Follow the Presentation Requirements section below.
Validation Workflow
Every generated configuration MUST be validated.
Incremental Validation
After generating root.hcl:
cd <infrastructure-directory>
terragrunt hcl fmt --check
After generating each child module:
cd <module-directory>
terragrunt hcl fmt --check
# If no dependencies on other modules:
terragrunt hcl validate --inputs
Full Validation
After all files are generated:
- Invoke the
terragrunt-validatorskill - If validation fails: analyze errors, fix, and re-validate until all pass
- If validation succeeds: present configurations with usage instructions
Skip validation only for: Partial snippets, documentation examples, or explicit user request.
Presentation Requirements
After successful validation, present ALL of the following sections.
1. Directory Structure Summary
tree <infrastructure-directory>
2. Files Generated
| File | Purpose |
|------|---------|
| root.hcl | Shared configuration for all child modules (state backend, provider) |
| dev/env.hcl | Development environment variables |
| prod/env.hcl | Production environment variables |
| dev/vpc/terragrunt.hcl | VPC module for development |
| ... | ... |
3. Usage Instructions
## Usage Instructions
### Prerequisites
1. AWS credentials configured (`aws configure` or environment variables)
2. S3 bucket `<BUCKET_NAME>` exists for state storage
3. DynamoDB table `<TABLE_NAME>` exists for state locking
### Commands
cd <INFRASTRUCTURE_DIR>
terragrunt run --all init # Initialize all modules
cd <ENV>/vpc && terragrunt plan # Preview a specific module
terragrunt run --all plan # Preview all changes
terragrunt run --all apply # Apply changes (requires approval)
terragrunt run --all destroy # Destroy (use with extreme caution)
4. Environment-Specific Notes
## Environment Notes
### Required Environment Variables
| Variable | Description | Example |
|----------|-------------|---------|
| AWS_PROFILE | AWS CLI profile to use | `my-profile` |
| AWS_REGION | AWS region (or set in provider) | `us-east-1` |
### Prerequisites
- [ ] S3 bucket `<BUCKET_NAME>` must exist before first run
- [ ] DynamoDB table `<TABLE_NAME>` must exist for state locking
- [ ] IAM permissions for Terraform state management
### Production-Specific Protections
| Module | Protection | Description |
|--------|------------|-------------|
| prod/rds | `prevent_destroy = true` | Prevents accidental database deletion |
| prod/rds | `exclude { actions = ["destroy"] }` | Blocks destroy commands |
5. Next Steps (Optional)
Suggest what the user might want to do next (add more modules, customize configurations, etc.)
Best Practices
Reference references/best_practices.md in the terragrunt-validator skill for comprehensive guidelines.
Key principles:
- Use
includeblocks to inherit root configuration (DRY) - Always provide mock outputs for dependencies
- Enable state encryption (
encrypt = true) - Use
generateblocks for provider configuration - Specify bounded version constraints (
~> 5.0, not>= 5.0) for local/Git modules - Never hardcode credentials or secrets
- Configure retry logic for transient errors
Note on Version Constraints with Registry Modules: When using Terraform Registry modules (e.g.,
tfr:///terraform-aws-modules/vpc/aws?version=5.1.0), they typically define their ownrequired_providers. Omit generatingrequired_providersinroot.hclto avoid conflicts — the module’s pinned version provides the constraint.
Anti-patterns to avoid:
- Hardcoded account IDs, regions, or environment names
- Missing mock outputs for dependencies
- Duplicated configuration across modules
- Unencrypted state storage
- Missing or loose version constraints (except when using registry modules that define their own)
- Root.hcl trying to read env.hcl that doesn’t exist at root level
Deprecated Attributes
| Deprecated | Replacement | Reference |
|---|---|---|
skip |
exclude block |
Docs |
retryable_errors |
errors.retry block |
Docs |
run-all |
run --all |
Migration |
--terragrunt-* flags |
Unprefixed flags | CLI Reference |
TERRAGRUNT_* env vars |
TG_* env vars |
CLI Reference |
Common Issues
For troubleshooting guidance, see references/troubleshooting.md, which covers:
- Root.hcl cannot find env.hcl errors
- Provider conflicts with registry modules
- Feature flag validation errors
- Child module env.hcl resolution issues
Anti-Patterns
NEVER use path_relative_to_include() as a module source path
- WHY: This function returns a path relative to the INCLUDING file, not the included root file; using it as a module source creates path resolution failures that vary by directory depth.
- BAD:
path_relative_to_include()interpolated into the source string in a child module’sterraform.source. - GOOD: Use
get_parent_terragrunt_dir()or construct explicit relative paths from the rootterragrunt.hcllocation.
NEVER duplicate backend configuration in every unit’s terragrunt.hcl
- WHY: Copy-pasted
remote_state {}blocks drift across units over time, creating inconsistent state key schemes and locking configurations that are difficult to audit. - BAD: A full
remote_state { backend = "s3" ... }block repeated in every leaf module. - GOOD: Define remote state once in the root
root.hcland inherit it in every unit viainclude "root" { path = find_in_parent_folders("root.hcl") }.
NEVER ignore dependency output mismatches between plan and apply
- WHY: When a
dependency.outputsreference is evaluated and the dependency has not been applied, Terragrunt substitutesmock_outputssilently; if mock types differ from actual output types, the apply will fail with a type error. - BAD: Leave
mock_outputsblocks with placeholder types and dismissmock_outputssubstitution warnings duringterragrunt plan. - GOOD: Define
mock_outputswhose types exactly match the actual dependency outputs, and usemock_outputs_allowed_terraform_commands = ["validate", "plan"]to limit substitution scope.
NEVER run terragrunt run --all apply without awareness of external dependency scope
- WHY: By default, units outside the current directory tree are excluded from
run --alloperations, which can produce partial applies that leave infrastructure in an inconsistent state. - BAD: Run
terragrunt run --all applyfrom a subdirectory expecting all transitive dependencies to be included automatically. - GOOD: Run from the repository root or pass
--terragrunt-include-external-dependenciesexplicitly to ensure the full dependency graph is evaluated.
NEVER use the same terragrunt.hcl for both dev and prod environments without environment-level variable overrides
- WHY: Sharing configuration without environment isolation causes prod deployments to silently inherit dev defaults (instance sizes, replica counts, retention policies).
- BAD: A single
terragrunt.hclwith noinputsblock differentiation between environments. - GOOD: Use environment-level
env.hclfiles withinputs = { environment = "prod", instance_type = "m5.xlarge" }overrides that layer on top of shared defaults from the root configuration.
NEVER write a feature flag default that references local.*
- WHY: Feature flag defaults must be static values, so a dynamic reference fails at evaluation time.
- BAD:
feature "enable_monitoring" { default = local.env.locals.enable_monitoring } - GOOD:
feature "enable_monitoring" { default = false }, then override at run time with--feature enable_monitoring=true.
NEVER make an environment-agnostic root.hcl read env.hcl
- WHY:
env.hcldoes not exist at the root level in Pattern A, so the path lookup fails and every child module breaks. - BAD:
read_terragrunt_config(find_in_parent_folders("env.hcl"))insideroot.hclfor a multi-environment layout. - GOOD: Read
env.hclfrom the child module and keeproot.hclto static values orget_env().
NEVER use deprecated skip, retryable_errors or run-all
- WHY: These have replacements (
exclude,errors.retry,run --all) and strict mode turns the deprecated forms into errors. - BAD:
skip = truein a unit, orterragrunt run-all planin usage instructions. - GOOD: Use an
excludeblock, anerrorsblock with retry, andterragrunt run --all plan.
NEVER generate a remote_state block without encrypt = true
- WHY: Unencrypted state can contain secrets in plain text, and the generation checklist requires encryption on every root.
- BAD:
remote_stateconfig inroot.hclwith noencryptsetting. - GOOD: Set
encrypt = truein theremote_stateconfig and generate the backend from the root template.
NEVER leave a production module without destroy protection
- WHY: An accidental
destroyon a database or similar resource is irreversible, and the generation checklist expects protection on production modules. - BAD: A
prod/rdsunit with noprevent_destroyand noexcludeblock. - GOOD: Set
prevent_destroy = trueand anexcludeblock withactions = ["destroy"]on production modules.
References
Templates — Read Before Generating
| Configuration Type | Template File | When to Read |
|---|---|---|
| Root configuration | assets/templates/root/terragrunt.hcl |
Before generating any root.hcl |
| Child module | assets/templates/child/terragrunt.hcl |
Before generating any child module |
| Standalone module | assets/templates/module/terragrunt.hcl |
Before generating standalone modules |
| Stack file | assets/templates/stack/terragrunt.stack.hcl |
Before generating stacks |
| Catalog unit | assets/templates/catalog/terragrunt.hcl |
Before generating catalog units |
References
| Reference | Content | When to Read |
|---|---|---|
references/common-patterns.md |
All generation patterns with examples | Always, before generating |
references/troubleshooting.md |
Common issues and fixes | When encountering errors |
terragrunt-validator skill, references/best_practices.md |
Comprehensive best practices | Always, before generating |
Official Documentation
Explicit NEVER/WHY/consequence triplets with side-by-side bad/good examples. Generic warnings score low — concrete failure modes with real impact (security, correctness, performance) score high.
Runtime validation via eval scenarios proving the skill actually changes agent behaviour. Requires evals/ directory, instructions.json, ≥3 scenarios, and ≥80% instruction coverage in summary.json.
Appropriate rigidity for the skill type: strict rules for critical/security skills, balanced steps for process skills, flexible options for tool skills. Mismatched calibration (too rigid or too loose) reduces score.
Expert-only knowledge the AI couldn't derive on its own — domain-specific patterns, production gotchas, decision frameworks, and non-obvious tool behaviour. High scores mean <5% redundancy with what the model already knows.
A clear philosophy plus numbered step-by-step workflows with explicit entry/exit points. Should answer: what is the core principle, how to execute it, and when (and when not) to apply it.
Rich activation keywords and trigger scenarios in the description so the skill fires when needed. Best descriptions read like an exhaustive list of the tasks and concepts that should invoke this skill.
Copy-paste-ready code, complete runnable examples, real file paths and commands. Pseudocode and abstract descriptions score low — concrete, executable content scores high.
SKILL.md as a <100-line navigation hub pointing to focused references/ files. Content should be lazy-loaded on demand, not front-loaded. Includes actionable 'When to Use' conditions per reference.
Proper frontmatter, single-task focus, activation keywords, and cross-harness portability. Skills must work across 40+ agentic harnesses without hardcoded paths or agent-specific references.
Expert-only knowledge the AI couldn't derive on its own — domain-specific patterns, production gotchas, decision frameworks, and non-obvious tool behaviour. High scores mean <5% redundancy with what the model already knows.
A clear philosophy plus numbered step-by-step workflows with explicit entry/exit points. Should answer: what is the core principle, how to execute it, and when (and when not) to apply it.
Explicit NEVER/WHY/consequence triplets with side-by-side bad/good examples. Generic warnings score low — concrete failure modes with real impact (security, correctness, performance) score high.
Proper frontmatter, single-task focus, activation keywords, and cross-harness portability. Skills must work across 40+ agentic harnesses without hardcoded paths or agent-specific references.
SKILL.md as a <100-line navigation hub pointing to focused references/ files. Content should be lazy-loaded on demand, not front-loaded. Includes actionable 'When to Use' conditions per reference.
Appropriate rigidity for the skill type: strict rules for critical/security skills, balanced steps for process skills, flexible options for tool skills. Mismatched calibration (too rigid or too loose) reduces score.
Rich activation keywords and trigger scenarios in the description so the skill fires when needed. Best descriptions read like an exhaustive list of the tasks and concepts that should invoke this skill.
Copy-paste-ready code, complete runnable examples, real file paths and commands. Pseudocode and abstract descriptions score low — concrete, executable content scores high.
No eval scenarios available.
Terragrunt Common Generation Patterns
Overview
This reference provides common patterns and code examples for generating Terragrunt configurations. Use these patterns as building blocks when creating new Terragrunt resources.
Root Configuration Patterns
Pattern 1: Basic Root with S3 Backend
Use when: Starting a new Terragrunt project with AWS S3 backend
# Root terragrunt.hcl
remote_state {
backend = "s3"
config = {
bucket = "company-terraform-state"
key = "${path_relative_to_include()}/terraform.tfstate"
region = "us-east-1"
encrypt = true
dynamodb_table = "terraform-locks"
}
generate = {
path = "backend.tf"
if_exists = "overwrite_terragrunt"
}
}
generate "provider" {
path = "provider.tf"
if_exists = "overwrite_terragrunt"
contents = <<EOF
provider "aws" {
region = var.region
}
EOF
}
inputs = {
region = "us-east-1"
common_tags = {
ManagedBy = "Terragrunt"
}
}
Pattern 2: Multi-Account Root Configuration
Use when: Managing multiple AWS accounts with role assumption
# Root terragrunt.hcl
locals {
account_vars = read_terragrunt_config(find_in_parent_folders("account.hcl"))
region_vars = read_terragrunt_config(find_in_parent_folders("region.hcl"))
env_vars = read_terragrunt_config(find_in_parent_folders("env.hcl"))
account_id = local.account_vars.locals.account_id
region = local.region_vars.locals.region
environment = local.env_vars.locals.environment
}
remote_state {
backend = "s3"
config = {
bucket = "terraform-state-${local.account_id}"
key = "${path_relative_to_include()}/terraform.tfstate"
region = local.region
encrypt = true
dynamodb_table = "terraform-locks-${local.environment}"
role_arn = "arn:aws:iam::${local.account_id}:role/TerraformRole"
}
generate = {
path = "backend.tf"
if_exists = "overwrite_terragrunt"
}
}
generate "provider" {
path = "provider.tf"
if_exists = "overwrite_terragrunt"
contents = <<EOF
provider "aws" {
region = "${local.region}"
assume_role {
role_arn = "arn:aws:iam::${local.account_id}:role/TerraformRole"
}
default_tags {
tags = {
Environment = "${local.environment}"
ManagedBy = "Terragrunt"
}
}
}
EOF
}
inputs = {
account_id = local.account_id
region = local.region
environment = local.environment
}
Pattern 3: Multi-Cloud Root Configuration
Use when: Managing resources across multiple cloud providers
# Root terragrunt.hcl
generate "providers" {
path = "providers.tf"
if_exists = "overwrite_terragrunt"
contents = <<EOF
terraform {
required_version = ">= 1.6.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
azurerm = {
source = "hashicorp/azurerm"
version = "~> 3.0"
}
google = {
source = "hashicorp/google"
version = "~> 5.0"
}
}
}
provider "aws" {
region = var.aws_region
}
provider "azurerm" {
features {}
subscription_id = var.azure_subscription_id
}
provider "google" {
project = var.gcp_project
region = var.gcp_region
}
EOF
}
Child Module Patterns
Pattern 1: Simple Module with No Dependencies
Use when: Creating standalone infrastructure component
# modules/vpc/terragrunt.hcl
include "root" {
path = find_in_parent_folders()
}
terraform {
source = "tfr:///terraform-aws-modules/vpc/aws?version=5.1.0"
}
inputs = {
name = "my-vpc"
cidr = "10.0.0.0/16"
azs = ["us-east-1a", "us-east-1b", "us-east-1c"]
private_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
public_subnets = ["10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24"]
enable_nat_gateway = true
enable_vpn_gateway = false
tags = {
Name = "my-vpc"
}
}
Pattern 2: Module with Single Dependency
Use when: Creating a resource that depends on another module’s outputs
# modules/rds/terragrunt.hcl
include "root" {
path = find_in_parent_folders()
}
terraform {
source = "tfr:///terraform-aws-modules/rds/aws?version=6.1.0"
}
dependency "vpc" {
config_path = "../vpc"
mock_outputs = {
vpc_id = "vpc-mock123"
database_subnet_ids = ["subnet-mock1", "subnet-mock2"]
}
mock_outputs_allowed_terraform_commands = ["validate", "plan", "destroy"]
}
dependency "security_group" {
config_path = "../security-groups/database"
mock_outputs = {
security_group_id = "sg-mock123"
}
mock_outputs_allowed_terraform_commands = ["validate", "plan", "destroy"]
}
inputs = {
identifier = "mydb"
engine = "postgres"
vpc_security_group_ids = [dependency.security_group.outputs.security_group_id]
db_subnet_group_name = dependency.vpc.outputs.database_subnet_group_name
allocated_storage = 20
instance_class = "db.t3.micro"
}
Pattern 3: Module with Multiple Dependencies
Use when: Creating complex infrastructure with multiple upstream dependencies
# modules/eks/terragrunt.hcl
include "root" {
path = find_in_parent_folders()
}
terraform {
source = "tfr:///terraform-aws-modules/eks/aws?version=19.15.0"
}
dependencies {
paths = ["../vpc", "../security-groups", "../iam-roles"]
}
dependency "vpc" {
config_path = "../vpc"
mock_outputs = {
vpc_id = "vpc-mock"
private_subnet_ids = ["subnet-1", "subnet-2"]
}
mock_outputs_allowed_terraform_commands = ["validate", "plan"]
}
dependency "security_groups" {
config_path = "../security-groups"
mock_outputs = {
cluster_security_group_id = "sg-mock"
}
mock_outputs_allowed_terraform_commands = ["validate", "plan"]
}
dependency "iam" {
config_path = "../iam-roles"
mock_outputs = {
cluster_role_arn = "arn:aws:iam::123456789012:role/mock-role"
}
mock_outputs_allowed_terraform_commands = ["validate", "plan"]
}
inputs = {
cluster_name = "my-eks-cluster"
cluster_version = "1.28"
vpc_id = dependency.vpc.outputs.vpc_id
subnet_ids = dependency.vpc.outputs.private_subnet_ids
cluster_security_group_id = dependency.security_groups.outputs.cluster_security_group_id
iam_role_arn = dependency.iam.outputs.cluster_role_arn
enable_irsa = true
}
Pattern 4: Module with Conditional Logic
Use when: Generating configurations with environment-specific variations
# modules/app/terragrunt.hcl
include "root" {
path = find_in_parent_folders()
}
locals {
env = get_env("ENVIRONMENT", "dev")
instance_counts = {
dev = 1
staging = 2
prod = 3
}
instance_types = {
dev = "t3.micro"
staging = "t3.small"
prod = "t3.medium"
}
}
terraform {
source = "../../terraform-modules/app"
}
inputs = {
environment = local.env
instance_count = local.instance_counts[local.env]
instance_type = local.instance_types[local.env]
enable_monitoring = local.env == "prod" ? true : false
enable_backups = local.env == "prod" ? true : false
tags = merge(
{
Environment = local.env
ManagedBy = "Terragrunt"
},
local.env == "prod" ? { CriticalResource = "true" } : {}
)
}
Environment-Specific Patterns
Pattern 1: Environment Configuration Files
Use when: Managing multiple environments with shared structure
infrastructure/
├── terragrunt.hcl # Root config
├── _env/
│ ├── prod.hcl # Production variables
│ ├── staging.hcl # Staging variables
│ └── dev.hcl # Development variables
├── prod/
│ ├── env.hcl -> ../_env/prod.hcl
│ └── vpc/
│ └── terragrunt.hcl
└── staging/
├── env.hcl -> ../_env/staging.hcl
└── vpc/
└── terragrunt.hcl
_env/prod.hcl:
locals {
environment = "prod"
region = "us-east-1"
vpc_cidr = "10.0.0.0/16"
instance_type = "t3.medium"
min_size = 3
max_size = 10
}
prod/vpc/terragrunt.hcl:
include "root" {
path = find_in_parent_folders()
}
locals {
env = read_terragrunt_config(find_in_parent_folders("env.hcl"))
}
terraform {
source = "tfr:///terraform-aws-modules/vpc/aws?version=5.1.0"
}
inputs = {
name = "${local.env.locals.environment}-vpc"
cidr = local.env.locals.vpc_cidr
azs = ["${local.env.locals.region}a", "${local.env.locals.region}b"]
}
Advanced Patterns
Pattern 1: Dynamic Provider Configuration
Use when: Provider configuration varies by module or environment
# modules/cross-account-resource/terragrunt.hcl
include "root" {
path = find_in_parent_folders()
}
locals {
target_account_id = "987654321098"
}
generate "provider_override" {
path = "provider_override.tf"
if_exists = "overwrite"
contents = <<EOF
provider "aws" {
alias = "target_account"
region = var.region
assume_role {
role_arn = "arn:aws:iam::${local.target_account_id}:role/CrossAccountRole"
}
}
EOF
}
terraform {
source = "../../terraform-modules/cross-account-resource"
}
Pattern 2: Module Composition
Use when: Combining multiple modules in a single configuration
# modules/application-stack/terragrunt.hcl
include "root" {
path = find_in_parent_folders()
}
terraform {
source = "../../terraform-modules/application-stack"
}
dependency "vpc" {
config_path = "../networking/vpc"
mock_outputs = {
vpc_id = "vpc-mock"
subnet_ids = ["subnet-1", "subnet-2"]
}
mock_outputs_allowed_terraform_commands = ["validate", "plan"]
}
dependency "database" {
config_path = "../data/rds"
mock_outputs = {
endpoint = "mock.endpoint.rds.amazonaws.com"
port = 5432
}
mock_outputs_allowed_terraform_commands = ["validate", "plan"]
}
dependency "cache" {
config_path = "../data/elasticache"
mock_outputs = {
endpoint = "mock.cache.amazonaws.com"
}
mock_outputs_allowed_terraform_commands = ["validate", "plan"]
}
inputs = {
name = "my-application"
# Networking
vpc_id = dependency.vpc.outputs.vpc_id
subnet_ids = dependency.vpc.outputs.private_subnet_ids
# Database
database_endpoint = dependency.database.outputs.endpoint
database_port = dependency.database.outputs.port
# Cache
cache_endpoint = dependency.cache.outputs.endpoint
# Application configuration
image_tag = "latest"
desired_count = 2
cpu = 256
memory = 512
}
Pattern 3: Hooks for Pre/Post Operations
Use when: Need to run commands before or after Terraform operations
# modules/database/terragrunt.hcl
include "root" {
path = find_in_parent_folders()
}
terraform {
source = "tfr:///terraform-aws-modules/rds/aws?version=6.1.0"
before_hook "backup_check" {
commands = ["apply"]
execute = ["bash", "-c", "echo 'Starting database deployment...'"]
}
after_hook "notify_deployment" {
commands = ["apply"]
execute = ["bash", "-c", "curl -X POST https://slack.webhook.url -d '{\"text\":\"Database deployed\"}'"]
run_on_error = false
}
error_hook "notify_error" {
commands = ["apply", "plan"]
execute = ["bash", "-c", "echo 'Error occurred during Terraform operation'"]
}
}
Pattern 4: External Data Integration
Use when: Need to fetch dynamic values from external sources
# modules/app/terragrunt.hcl
include "root" {
path = find_in_parent_folders()
}
locals {
# Fetch current git branch
git_branch = run_cmd("--terragrunt-quiet", "git", "rev-parse", "--abbrev-ref", "HEAD")
# Fetch AWS account ID
account_id = run_cmd("--terragrunt-quiet", "aws", "sts", "get-caller-identity", "--query", "Account", "--output", "text")
# Read JSON configuration
config = jsondecode(file("${get_terragrunt_dir()}/config.json"))
}
terraform {
source = "../../terraform-modules/app"
}
inputs = {
git_branch = local.git_branch
account_id = local.account_id
app_config = local.config
name = "${local.config.app_name}-${local.git_branch}"
}
Custom Provider Patterns
Pattern 1: Kubernetes Provider with EKS
Use when: Managing Kubernetes resources with Terragrunt
# modules/k8s-app/terragrunt.hcl
include "root" {
path = find_in_parent_folders()
}
dependency "eks" {
config_path = "../eks-cluster"
mock_outputs = {
cluster_endpoint = "https://mock-endpoint"
cluster_certificate = "mock-cert"
cluster_name = "mock-cluster"
}
mock_outputs_allowed_terraform_commands = ["validate", "plan"]
}
generate "kubernetes_provider" {
path = "kubernetes_provider.tf"
if_exists = "overwrite"
contents = <<EOF
provider "kubernetes" {
host = "${dependency.eks.outputs.cluster_endpoint}"
cluster_ca_certificate = base64decode("${dependency.eks.outputs.cluster_certificate}")
exec {
api_version = "client.authentication.k8s.io/v1beta1"
command = "aws"
args = [
"eks",
"get-token",
"--cluster-name",
"${dependency.eks.outputs.cluster_name}"
]
}
}
provider "helm" {
kubernetes {
host = "${dependency.eks.outputs.cluster_endpoint}"
cluster_ca_certificate = base64decode("${dependency.eks.outputs.cluster_certificate}")
exec {
api_version = "client.authentication.k8s.io/v1beta1"
command = "aws"
args = [
"eks",
"get-token",
"--cluster-name",
"${dependency.eks.outputs.cluster_name}"
]
}
}
}
EOF
}
terraform {
source = "../../terraform-modules/k8s-app"
}
Pattern 2: Multiple Provider Versions
Use when: Different modules require different provider versions
# modules/legacy-resource/terragrunt.hcl
include "root" {
path = find_in_parent_folders()
}
generate "provider_version_override" {
path = "versions.tf"
if_exists = "overwrite"
contents = <<EOF
terraform {
required_version = ">= 1.3.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.0" # Legacy version for compatibility
}
}
}
EOF
}
terraform {
source = "../../terraform-modules/legacy-resource"
}
Stacks Patterns (2025)
Terragrunt Stacks allow you to define infrastructure blueprints that generate unit configurations programmatically. GA since v0.78.0 (May 2025).
Pattern 1: Basic Stack with Units
Use when: Creating a reusable infrastructure blueprint
# terragrunt.stack.hcl
locals {
environment = "prod"
aws_region = "us-east-1"
units_path = find_in_parent_folders("catalog/units")
}
unit "vpc" {
source = "${local.units_path}/vpc"
path = "vpc"
values = {
vpc_name = "${local.environment}-vpc"
cidr = "10.0.0.0/16"
environment = local.environment
}
}
unit "database" {
source = "${local.units_path}/database"
path = "database"
values = {
db_name = "${local.environment}-db"
engine = "postgres"
vpc_path = "../vpc"
environment = local.environment
}
}
Pattern 2: Stack with Git-Based Unit Sources
Use when: Using versioned unit definitions from a remote repository
# terragrunt.stack.hcl
unit "vpc" {
source = "git::git@github.com:acme/infrastructure-catalog.git//units/vpc?ref=v1.0.0"
path = "vpc"
values = {
vpc_name = "main"
cidr = "10.0.0.0/16"
}
}
unit "database" {
source = "git::git@github.com:acme/infrastructure-catalog.git//units/database?ref=v1.0.0"
path = "database"
values = {
engine = "postgres"
version = "15"
vpc_path = "../vpc"
}
}
Pattern 3: Catalog Unit with Values
Use when: Creating reusable unit templates for stacks
# catalog/units/vpc/terragrunt.hcl
include "root" {
path = find_in_parent_folders("root.hcl")
}
terraform {
source = "tfr:///terraform-aws-modules/vpc/aws?version=5.1.0"
}
inputs = {
name = values.vpc_name
cidr = values.cidr
azs = ["${values.aws_region}a", "${values.aws_region}b"]
private_subnets = ["10.0.1.0/24", "10.0.2.0/24"]
public_subnets = ["10.0.101.0/24", "10.0.102.0/24"]
enable_nat_gateway = try(values.enable_nat, true)
tags = {
Environment = values.environment
ManagedBy = "Terragrunt"
}
}
Pattern 4: Stack Commands
# Generate unit configurations from stack
terragrunt stack generate
# Plan all units in the stack
terragrunt stack run plan
# Apply all units in the stack
terragrunt stack run apply
# Get aggregated outputs from all units
terragrunt stack output
# Clean generated directories
terragrunt stack clean
Feature Flags Patterns (2025)
Feature flags provide runtime control over Terragrunt behavior.
Pattern 1: Basic Feature Flag
Use when: Enabling/disabling features at runtime
# terragrunt.hcl
feature "enable_monitoring" {
default = false
}
inputs = {
enable_monitoring = feature.enable_monitoring.value
}
Usage:
# Override via CLI
terragrunt apply --feature enable_monitoring=true
# Override via environment variable
export TG_FEATURE="enable_monitoring=true"
terragrunt apply
Pattern 2: Feature Flag for Module Versioning
Use when: Controlling module versions at runtime
# terragrunt.hcl
feature "module_version" {
default = "v1.0.0"
}
terraform {
source = "git::git@github.com:acme/modules.git//vpc?ref=${feature.module_version.value}"
}
Pattern 3: Feature Flag with Conditional Logic
Use when: Complex conditional behavior based on flags
# terragrunt.hcl
feature "enable_ha" {
default = false
}
locals {
instance_count = feature.enable_ha.value ? 3 : 1
instance_type = feature.enable_ha.value ? "t3.medium" : "t3.micro"
}
inputs = {
instance_count = local.instance_count
instance_type = local.instance_type
}
Pattern 4: Environment-Based Feature Flags
Use when: Controlling deployments per environment
# prod/root.hcl
feature "prod" {
default = false
}
exclude {
if = !feature.prod.value
actions = ["all_except_output"]
}
Usage:
# Enable production deployment
terragrunt run --all apply --feature prod=true
Exclude Block Patterns (2025)
The exclude block replaces the deprecated skip attribute with more fine-grained control.
Pattern 1: Basic Exclusion
Use when: Excluding a unit from all operations
# terragrunt.hcl
exclude {
if = true
actions = ["all"]
exclude_dependencies = false
}
Pattern 2: Exclude Specific Actions
Use when: Excluding only certain operations
# terragrunt.hcl
exclude {
if = true
actions = ["apply", "destroy"] # Still allows plan and output
}
Pattern 3: Conditional Exclusion with Feature Flags
Use when: Dynamic exclusion based on runtime flags
# terragrunt.hcl
feature "skip_in_dev" {
default = false
}
exclude {
if = feature.skip_in_dev.value
actions = ["apply", "destroy"]
exclude_dependencies = false
}
Pattern 4: Time-Based Exclusion
Use when: Preventing deployments during certain periods
# terragrunt.hcl
locals {
day_of_week = formatdate("EEE", timestamp())
is_weekend = contains(["Fri", "Sat", "Sun"], local.day_of_week)
}
exclude {
if = local.is_weekend
actions = ["apply", "destroy"]
}
Pattern 5: All Except Output
Use when: Allowing only output retrieval
# terragrunt.hcl
exclude {
if = true
actions = ["all_except_output"]
}
Errors Block Patterns (2025)
The errors block replaces deprecated retryable_errors, retry_max_attempts, and retry_sleep_interval_sec.
Pattern 1: Basic Retry Configuration
Use when: Handling transient errors with retries
# terragrunt.hcl
errors {
retry "transient_errors" {
retryable_errors = [
"(?s).*Failed to load state.*tcp.*timeout.*",
"(?s).*Error installing provider.*TLS handshake timeout.*",
"(?s).*429 Too Many Requests.*",
]
max_attempts = 3
sleep_interval_sec = 5
}
}
Pattern 2: Ignore Safe Errors
Use when: Ignoring known safe-to-ignore errors
# terragrunt.hcl
errors {
ignore "known_warnings" {
ignorable_errors = [
".*Warning: Resource already exists.*",
"!.*Error: critical.*" # Negation: don't ignore critical errors
]
message = "Ignoring known safe warnings"
signals = {
alert_team = false
}
}
}
Pattern 3: Combined Retry and Ignore
Use when: Comprehensive error handling
# terragrunt.hcl
errors {
retry "network_errors" {
retryable_errors = [
"(?s).*connection reset by peer.*",
"(?s).*timeout.*",
]
max_attempts = 3
sleep_interval_sec = 10
}
ignore "deprecation_warnings" {
ignorable_errors = [
".*Deprecation Warning.*",
]
message = "Ignoring deprecation warnings"
}
}
Pattern 4: Feature Flag Controlled Error Handling
Use when: Dynamic error handling based on flags
# terragrunt.hcl
feature "enable_flaky_module" {
default = false
}
errors {
ignore "flaky_module_errors" {
ignorable_errors = feature.enable_flaky_module.value ? [
".*Error: flaky module error.*"
] : []
message = "Ignoring flaky module error"
signals = {
send_notification = true
}
}
}
OpenTofu Engine Patterns (2025)
Configure Terragrunt to use OpenTofu as the IaC engine.
Pattern 1: GitHub-Based Engine
Use when: Using the official OpenTofu engine
# terragrunt.hcl
engine {
source = "github.com/gruntwork-io/terragrunt-engine-opentofu"
version = "v0.0.15"
}
Pattern 2: Auto-Install OpenTofu Version
Use when: Automatically installing a specific OpenTofu version
# terragrunt.hcl
engine {
source = "github.com/gruntwork-io/terragrunt-engine-opentofu"
meta = {
tofu_version = "v1.9.1" # Or "latest" for stable version
tofu_install_dir = "/opt/tofu" # Optional custom install directory
}
}
Pattern 3: Local Engine Binary
Use when: Using a locally built or installed engine
# terragrunt.hcl
engine {
source = "/usr/local/bin/terragrunt-iac-engine-opentofu"
}
Pattern 4: HTTPS Engine Source
Use when: Downloading engine from a specific URL
# terragrunt.hcl
engine {
source = "https://github.com/gruntwork-io/terragrunt-engine-opentofu/releases/download/v0.0.15/terragrunt-iac-engine-opentofu_rpc_v0.0.15_linux_amd64.zip"
}
Provider Cache Patterns (2025)
Optimize provider downloads with caching.
Pattern 1: Enable Provider Cache Server
Use when: Running multiple terragrunt operations
# Enable provider cache for run --all operations
terragrunt run --all plan --provider-cache
# Via environment variable
TG_PROVIDER_CACHE=1 terragrunt run --all apply
Pattern 2: Custom Cache Directory
Use when: Specifying a custom cache location
TG_PROVIDER_CACHE=1 \
TG_PROVIDER_CACHE_DIR=/custom/cache/path \
terragrunt plan
Pattern 3: Auto Provider Cache (OpenTofu 1.10+)
Use when: Using OpenTofu’s native provider caching
# Enable auto-provider-cache-dir experiment
terragrunt run --all apply --experiment auto-provider-cache-dir
# Via environment variable
TG_EXPERIMENT='auto-provider-cache-dir' terragrunt run --all apply
Pattern 4: Remote Cache Server
Use when: Sharing cache across team/CI
TG_PROVIDER_CACHE=1 \
TG_PROVIDER_CACHE_HOST=192.168.0.100 \
TG_PROVIDER_CACHE_PORT=5758 \
TG_PROVIDER_CACHE_TOKEN=my-secret \
terragrunt apply
Summary
These patterns cover the most common Terragrunt generation scenarios:
- Root configurations - Project setup with state management
- Child modules - Resource creation with dependency management
- Environment handling - Multi-environment infrastructure
- Advanced patterns - Complex scenarios and integrations
- Stacks - Infrastructure blueprints for maximum reusability (2025)
- Feature Flags - Runtime control over behavior (2025)
- Exclude blocks - Fine-grained execution control (2025)
- Errors blocks - Advanced error handling (2025)
- OpenTofu engine - Alternative IaC engine support (2025)
- Provider cache - Performance optimization (2025)
When generating Terragrunt configurations, select the appropriate pattern based on:
- Project structure (single vs multi-account/environment)
- Module dependencies (none, single, multiple)
- Provider requirements (single vs multi-cloud)
- Operational needs (hooks, external data, etc.)
- Reusability requirements (stacks vs traditional modules)
- Runtime control needs (feature flags, exclusions)
Always validate generated configurations using the terragrunt-validator or terraform-validator skills.
Terragrunt Troubleshooting Guide
Root.hcl Cannot Find env.hcl
Symptom: Error: Attempt to get attribute from null value in root.hcl
Cause: Root.hcl is trying to read env.hcl via find_in_parent_folders("env.hcl"), but env.hcl doesn’t exist at the root level.
Solution: Make root.hcl environment-agnostic:
# DON'T do this in root.hcl for multi-environment setups:
locals {
env_vars = read_terragrunt_config(find_in_parent_folders("env.hcl")) # FAILS
}
# DO use static values or get_env():
generate "provider" {
path = "provider.tf"
if_exists = "overwrite_terragrunt"
contents = <<EOF
provider "aws" {
region = "us-east-1" # Or: get_env("AWS_REGION", "us-east-1")
}
EOF
}
Provider Conflict with Registry Modules
Symptom: Error: Duplicate required providers configuration
Solutions:
-
Remove conflicting generate block — Only generate
provider "aws", notrequired_providers, when using registry modules:generate "provider" { path = "provider.tf" if_exists = "overwrite_terragrunt" contents = <<EOF provider "aws" { region = "us-east-1" } EOF } -
Use
if_exists = "skip"— Skip generation if the file already exists:generate "versions" { path = "versions.tf" if_exists = "skip" contents = "..." } -
Clear cache if conflicts persist:
rm -rf .terragrunt-cache && terragrunt init
Feature Flag Validation Errors
Symptom: Unknown variable; There is no variable named "local" in feature blocks
Cause: Feature flag defaults must be static values, not references to locals or variables.
Solution: Ensure defaults are static values (see Feature Flags section in SKILL.md).
Child Module Cannot Find env.hcl
Symptom: Error: Attempt to get attribute from null value in a child module
Cause: Child module is trying to read env.hcl but the file doesn’t exist in the environment directory.
Solution: Ensure env.hcl exists in the environment directory:
dev/
├── env.hcl # MUST exist
└── vpc/
└── terragrunt.hcl # Calls find_in_parent_folders("env.hcl")