Terragrunt Validator Overview This skill provides comprehensive validation, linting, and testing capabilities for Terragrunt configurations. Terragrunt is a thin wrapper for Terraform/OpenTofu that provides extra tools for keeping configurations DRY (Don't Repeat Yourself), working with multiple modules, and managing remote state. Use this skill when: - Validating Terragrunt HCL files ( .hcl, terragrunt.hcl, terragrunt.stack.hcl) - Working with Terragrunt Stacks (unit/stack blocks, ) - Performing dry-run testing with - Linting Terragrunt/Terraform code for best practices - Detecting and resea…

, '', source)\n\n module_info = {\n 'name': Path(filepath).parent.name, # Use directory name as module name\n 'source': clean_source,\n 'version': version,\n 'file': str(filepath),\n 'type': self._categorize_module_source(clean_source)\n }\n\n if module_info['type'] in ['git', 'registry', 'http', 'custom', 'terragrunt']:\n self.custom_modules[clean_source].append(module_info)\n\n def _categorize_module_source(self, source: str) -> str:\n \"\"\"Categorize the module source type.\"\"\"\n if source.startswith('./') or source.startswith('../'):\n return 'local'\n elif source.startswith('tfr:///'):\n # Terragrunt registry format\n return 'terragrunt'\n elif source.startswith('git::') or 'github.com' in source or 'gitlab.com' in source:\n return 'git'\n elif source.startswith('http://') or source.startswith('https://'):\n return 'http'\n elif '/' in source and not source.startswith('.'):\n # Check if this looks like a provider source (org/name) vs module (org/name/provider)\n # Provider sources have exactly 2 path components (e.g., hashicorp/aws, datadog/datadog)\n # Module sources have 3+ components (e.g., terraform-aws-modules/vpc/aws)\n parts = source.split('/')\n if len(parts) == 2:\n # This looks like a provider source, not a module\n # Filter these out as they're detected separately in extract_providers\n return 'provider'\n else:\n # Likely Terraform Registry format (e.g., terraform-aws-modules/vpc/aws)\n return 'registry'\n else:\n return 'custom'\n\n def analyze_directory(self) -> None:\n \"\"\"Analyze all HCL files in the target directory.\"\"\"\n hcl_files = self.find_hcl_files()\n\n if not hcl_files:\n print(f\"No .hcl or .tf files found in {self.target_dir}\", file=sys.stderr)\n return\n\n for filepath in hcl_files:\n try:\n with open(filepath, 'r', encoding='utf-8') as f:\n content = f.read()\n self.extract_providers(content, filepath)\n self.extract_modules(content, filepath)\n except Exception as e:\n print(f\"Error reading {filepath}: {e}\", file=sys.stderr)\n\n def generate_report(self, output_format: str = 'text') -> str:\n \"\"\"Generate a report of custom resources found.\"\"\"\n if output_format == 'json':\n return self._generate_json_report()\n else:\n return self._generate_text_report()\n\n def _generate_json_report(self) -> str:\n \"\"\"Generate JSON format report.\"\"\"\n report = {\n 'custom_providers': {\n provider: sorted(list(versions))\n for provider, versions in sorted(self.custom_providers.items())\n },\n 'custom_modules': {\n source: modules\n for source, modules in sorted(self.custom_modules.items())\n }\n }\n return json.dumps(report, indent=2)\n\n def _generate_text_report(self) -> str:\n \"\"\"Generate human-readable text report.\"\"\"\n lines = []\n lines.append(\"=\" * 80)\n lines.append(\"Terragrunt Custom Resource Detection Report\")\n lines.append(\"=\" * 80)\n lines.append(\"\")\n\n # Custom Providers Section\n if self.custom_providers:\n lines.append(\"CUSTOM PROVIDERS DETECTED:\")\n lines.append(\"-\" * 80)\n for provider, versions in sorted(self.custom_providers.items()):\n lines.append(f\"\\nProvider: {provider}\")\n lines.append(f\" Versions: {', '.join(sorted(versions))}\")\n lines.append(f\" → Action: Resolve with Context7 library lookup:\")\n lines.append(f\" mcp__context7__resolve-library-id (libraryName: \\\"{provider} terraform provider\\\")\")\n lines.append(f\" mcp__context7__query-docs (query: \\\"authentication and configuration\\\")\")\n lines.append(f\" or search web: '{provider} terraform provider documentation'\")\n lines.append(\"\")\n else:\n lines.append(\"CUSTOM PROVIDERS: None detected\")\n lines.append(\"\")\n\n # Custom Modules Section\n if self.custom_modules:\n lines.append(\"CUSTOM MODULES DETECTED:\")\n lines.append(\"-\" * 80)\n for source, modules in sorted(self.custom_modules.items()):\n module_info = modules[0] # Take first occurrence for details\n lines.append(f\"\\nModule Source: {source}\")\n lines.append(f\" Type: {module_info['type']}\")\n lines.append(f\" Version: {module_info['version']}\")\n lines.append(f\" Used in: {module_info['file']}\")\n lines.append(f\" Used {len(modules)} time(s)\")\n\n # Provide search guidance based on module type\n if module_info['type'] == 'git':\n clean_source = source.replace('git::', '')\n lines.append(f\" → Action: Search repository docs for '{clean_source}'\")\n elif module_info['type'] == 'registry' or module_info['type'] == 'terragrunt':\n # Clean up tfr:/// prefix for registry lookup\n clean_source = source.replace('tfr:///', '')\n registry_source = clean_source.split('//')[0]\n lines.append(f\" → Action: Visit https://registry.terraform.io/modules/{registry_source}\")\n lines.append(f\" and resolve with Context7: \\\"{registry_source}\\\"\")\n else:\n lines.append(f\" → Action: Search for documentation related to this module source\")\n lines.append(\"\")\n else:\n lines.append(\"CUSTOM MODULES: None detected\")\n lines.append(\"\")\n\n # Summary\n lines.append(\"=\" * 80)\n lines.append(\"SUMMARY\")\n lines.append(\"=\" * 80)\n lines.append(f\"Custom Providers: {len(self.custom_providers)}\")\n lines.append(f\"Custom Modules: {len(self.custom_modules)}\")\n lines.append(\"\")\n\n if self.custom_providers or self.custom_modules:\n lines.append(\"RECOMMENDED ACTIONS:\")\n lines.append(\"1. Use WebSearch to look up documentation for each custom resource\")\n lines.append(\"2. Pay attention to version compatibility\")\n lines.append(\"3. Review provider/module documentation for required configuration\")\n lines.append(\"4. Check for any known issues or breaking changes\")\n else:\n lines.append(\"No custom providers or modules detected.\")\n lines.append(\"All resources appear to be standard HashiCorp providers or local modules.\")\n lines.append(\"\")\n\n return \"\\n\".join(lines)\n\n\ndef main():\n parser = argparse.ArgumentParser(\n description='Detect custom providers and modules in Terragrunt/Terraform configurations'\n )\n parser.add_argument(\n 'directory',\n nargs='?',\n default='.',\n help='Directory to analyze (default: current directory)'\n )\n parser.add_argument(\n '--format',\n choices=['text', 'json'],\n default='text',\n help='Output format (default: text)'\n )\n\n args = parser.parse_args()\n\n detector = ResourceDetector(args.directory)\n detector.analyze_directory()\n report = detector.generate_report(args.format)\n print(report)\n\n\nif __name__ == '__main__':\n main()\n","content_type":"text/x-python; charset=utf-8","language":"python","size":17860,"content_sha256":"ba67b7ee521698d638ae5c96afcceaadbb8d5eb26a0d7f63e74818e1533c2474"},{"filename":"scripts/run_ci_checks.sh","content":"#!/usr/bin/env bash\n#\n# Deterministic CI entrypoint for terragrunt-validator.\n# Runs syntax checks plus regression tests that do not depend on cloud access.\n\nset -euo pipefail\n\nSCRIPT_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd)\"\nreadonly SCRIPT_DIR\nSKILL_DIR=\"$(cd \"$SCRIPT_DIR/..\" && pwd)\"\nreadonly SKILL_DIR\n\nVALIDATOR_SCRIPT=\"$SKILL_DIR/scripts/validate_terragrunt.sh\"\nDETECTOR_SCRIPT=\"$SKILL_DIR/scripts/detect_custom_resources.py\"\nSHELL_REGRESSION_TEST=\"$SKILL_DIR/test/test_validate_terragrunt.sh\"\nPYTHON_REGRESSION_TEST=\"$SKILL_DIR/test/test_detect_custom_resources.py\"\nSELF_SCRIPT=\"$SCRIPT_DIR/$(basename \"${BASH_SOURCE[0]}\")\"\n\nusage() {\n cat \u003c\u003c'EOF'\nUsage: run_ci_checks.sh [OPTIONS]\n\nDeterministic CI checks for terragrunt-validator.\n\nOptions:\n --require-shellcheck Fail when shellcheck is unavailable.\n --skip-shellcheck Skip shellcheck stage.\n -h, --help Show this help message.\n\nEnvironment:\n CI=true|1 Defaults to --require-shellcheck unless overridden.\nEOF\n}\n\nis_true() {\n local value=\"${1:-}\"\n [[ \"$value\" == \"true\" || \"$value\" == \"1\" ]]\n}\n\nmain() {\n local require_shellcheck=0\n local skip_shellcheck=0\n local shellcheck_overridden=0\n\n while [[ $# -gt 0 ]]; do\n case \"$1\" in\n --require-shellcheck)\n require_shellcheck=1\n skip_shellcheck=0\n shellcheck_overridden=1\n ;;\n --skip-shellcheck)\n skip_shellcheck=1\n require_shellcheck=0\n shellcheck_overridden=1\n ;;\n -h|--help)\n usage\n exit 0\n ;;\n *)\n echo \"Error: unknown option '$1'\" >&2\n usage\n exit 1\n ;;\n esac\n shift\n done\n\n if [[ \"$shellcheck_overridden\" -eq 0 ]] && is_true \"${CI:-}\"; then\n require_shellcheck=1\n fi\n\n if [[ \"$skip_shellcheck\" -eq 1 && \"$require_shellcheck\" -eq 1 ]]; then\n echo \"Error: --skip-shellcheck and --require-shellcheck cannot be combined.\" >&2\n exit 1\n fi\n\n export LC_ALL=C\n export LANG=C\n export TZ=UTC\n\n echo \"[1/5] bash syntax checks\"\n bash -n \"$VALIDATOR_SCRIPT\" \"$SHELL_REGRESSION_TEST\" \"$SELF_SCRIPT\"\n\n echo \"[2/5] python syntax checks\"\n python3 -m py_compile \"$DETECTOR_SCRIPT\" \"$PYTHON_REGRESSION_TEST\"\n\n echo \"[3/5] python regression tests\"\n python3 \"$PYTHON_REGRESSION_TEST\"\n\n echo \"[4/5] shell regression tests\"\n bash \"$SHELL_REGRESSION_TEST\"\n\n echo \"[5/5] shellcheck\"\n if [[ \"$skip_shellcheck\" -eq 1 ]]; then\n echo \"ShellCheck: SKIP (--skip-shellcheck)\"\n elif command -v shellcheck >/dev/null 2>&1; then\n shellcheck \"$VALIDATOR_SCRIPT\" \"$SHELL_REGRESSION_TEST\" \"$SELF_SCRIPT\"\n echo \"ShellCheck: PASS\"\n elif [[ \"$require_shellcheck\" -eq 1 ]]; then\n echo \"ShellCheck: required but not installed\" >&2\n exit 1\n else\n echo \"ShellCheck: SKIP (not installed; use --require-shellcheck to enforce)\"\n fi\n\n echo \"PASS: terragrunt-validator CI checks\"\n}\n\nmain \"$@\"\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":3143,"content_sha256":"10b67b31101f88d6ca45169e53d4d94741f47a42a3a08f08172fa96fdd7547cd"},{"filename":"scripts/validate_terragrunt.sh","content":"#!/bin/bash\n\n# Terragrunt Validation Script\n# This script performs comprehensive validation of Terragrunt configurations including:\n# - HCL formatting checks\n# - HCL input validation (new in 0.93+)\n# - Terragrunt validation\n# - Terraform validation\n# - Linting with tflint\n# - Security scanning with Trivy (preferred) or tfsec (legacy)\n# - Dependency graph validation\n#\n# Designed for Terragrunt 0.93+ with the new CLI redesign\n\nset -euo pipefail\n\n# Colors for output\nRED='\\033[0;31m'\nGREEN='\\033[0;32m'\nYELLOW='\\033[1;33m'\nBLUE='\\033[0;34m'\nNC='\\033[0m' # No Color\n\n# Configuration\nTARGET_DIR_INPUT=\"${1:-.}\"\n# Convert to absolute path when target exists, keep input value otherwise\n# so main() can print a friendly, consistent error message.\nif [[ -d \"$TARGET_DIR_INPUT\" ]]; then\n TARGET_DIR=$(cd \"$TARGET_DIR_INPUT\" && pwd)\nelse\n TARGET_DIR=\"$TARGET_DIR_INPUT\"\nfi\nSKIP_PLAN=\"${SKIP_PLAN:-false}\"\nSKIP_SECURITY=\"${SKIP_SECURITY:-false}\"\nSKIP_LINT=\"${SKIP_LINT:-false}\"\nSKIP_INPUT_VALIDATION=\"${SKIP_INPUT_VALIDATION:-false}\"\nSKIP_INIT=\"${SKIP_INIT:-false}\"\nSKIP_BACKEND_INIT=\"${SKIP_BACKEND_INIT:-false}\"\nSOFT_FAIL_SECURITY=\"${SOFT_FAIL_SECURITY:-false}\"\n\n# Security scanner preference (trivy, tfsec, checkov, or auto)\nSECURITY_SCANNER=\"${SECURITY_SCANNER:-auto}\"\n\n# Build strict mode flag\nSTRICT_FLAG=\"\"\nif [[ \"${TG_STRICT_MODE:-false}\" == \"true\" ]]; then\n STRICT_FLAG=\"--strict-mode\"\nfi\n\nprint_header() {\n echo -e \"\\n${BLUE}===================================${NC}\"\n echo -e \"${BLUE}$1${NC}\"\n echo -e \"${BLUE}===================================${NC}\\n\"\n}\n\nprint_success() {\n echo -e \"${GREEN}✓ $1${NC}\"\n}\n\nprint_error() {\n echo -e \"${RED}✗ $1${NC}\"\n}\n\nprint_warning() {\n echo -e \"${YELLOW}⚠ $1${NC}\"\n}\n\nprint_info() {\n echo -e \"${BLUE}ℹ $1${NC}\"\n}\n\n# Detect execution mode from Terragrunt files in target directory.\n# Modes:\n# - multi: nested terragrunt units exist\n# - single: current dir is a single Terragrunt unit\n# - root-only: root.hcl exists but no unit in current directory\n# - none: no recognizable Terragrunt config found\ndetect_target_mode() {\n local has_direct_terragrunt=false\n local has_nested_terragrunt=false\n\n if [[ -f \"$TARGET_DIR/terragrunt.hcl\" || -f \"$TARGET_DIR/terragrunt.stack.hcl\" ]]; then\n has_direct_terragrunt=true\n fi\n\n if find \"$TARGET_DIR\" -mindepth 2 -type f \\\n \\( -name \"terragrunt.hcl\" -o -name \"terragrunt.stack.hcl\" \\) \\\n ! -path \"*/.terragrunt-cache/*\" | grep -q .; then\n has_nested_terragrunt=true\n fi\n\n if [[ \"$has_nested_terragrunt\" == \"true\" ]]; then\n echo \"multi\"\n elif [[ \"$has_direct_terragrunt\" == \"true\" ]]; then\n echo \"single\"\n elif [[ -f \"$TARGET_DIR/root.hcl\" ]]; then\n echo \"root-only\"\n else\n echo \"none\"\n fi\n}\n\n# Check if required tools are installed\ncheck_dependencies() {\n print_header \"Checking Dependencies\"\n\n local missing_tools=()\n\n if ! command -v terragrunt &> /dev/null; then\n missing_tools+=(\"terragrunt\")\n else\n local tg_version\n tg_version=$(terragrunt --version | head -n1)\n print_success \"terragrunt $tg_version\"\n\n # Check if version is >= 0.93 for new CLI\n if [[ \"$tg_version\" =~ v0\\.([0-9]+) ]]; then\n local minor_version=\"${BASH_REMATCH[1]}\"\n if (( minor_version \u003c 93 )); then\n print_warning \"Terragrunt version \u003c 0.93 detected. Some new CLI features may not work.\"\n print_info \"Consider upgrading to 0.93+ for best compatibility.\"\n fi\n fi\n fi\n\n if ! command -v terraform &> /dev/null; then\n if command -v tofu &> /dev/null; then\n print_success \"opentofu $(tofu version -json 2>/dev/null | jq -r '.terraform_version' 2>/dev/null || tofu --version | head -n1)\"\n else\n missing_tools+=(\"terraform or opentofu\")\n fi\n else\n print_success \"terraform $(terraform version -json 2>/dev/null | jq -r '.terraform_version' 2>/dev/null || terraform --version | head -n1)\"\n fi\n\n if [[ \"$SKIP_LINT\" != \"true\" ]] && ! command -v tflint &> /dev/null; then\n print_warning \"tflint not found - skipping lint checks\"\n SKIP_LINT=true\n elif [[ \"$SKIP_LINT\" != \"true\" ]]; then\n print_success \"tflint $(tflint --version | head -n1)\"\n fi\n\n # Check for security scanners\n if [[ \"$SKIP_SECURITY\" != \"true\" ]]; then\n local found_scanner=false\n\n if [[ \"$SECURITY_SCANNER\" == \"auto\" ]] || [[ \"$SECURITY_SCANNER\" == \"trivy\" ]]; then\n if command -v trivy &> /dev/null; then\n print_success \"trivy $(trivy --version 2>&1 | head -n1)\"\n SECURITY_SCANNER=\"trivy\"\n found_scanner=true\n fi\n fi\n\n if [[ \"$found_scanner\" == \"false\" ]] && { [[ \"$SECURITY_SCANNER\" == \"auto\" ]] || [[ \"$SECURITY_SCANNER\" == \"checkov\" ]]; }; then\n if command -v checkov &> /dev/null; then\n print_success \"checkov $(checkov --version 2>&1 | head -n1)\"\n SECURITY_SCANNER=\"checkov\"\n found_scanner=true\n fi\n fi\n\n if [[ \"$found_scanner\" == \"false\" ]] && { [[ \"$SECURITY_SCANNER\" == \"auto\" ]] || [[ \"$SECURITY_SCANNER\" == \"tfsec\" ]]; }; then\n if command -v tfsec &> /dev/null; then\n print_warning \"tfsec found but is deprecated - consider migrating to Trivy\"\n print_success \"tfsec $(tfsec --version 2>&1 | head -n1)\"\n SECURITY_SCANNER=\"tfsec\"\n found_scanner=true\n fi\n fi\n\n if [[ \"$found_scanner\" == \"false\" ]]; then\n print_warning \"No security scanner found (trivy, checkov, or tfsec) - skipping security checks\"\n print_info \"Install trivy: brew install trivy (macOS) or see https://trivy.dev\"\n SKIP_SECURITY=true\n fi\n fi\n\n if [ ${#missing_tools[@]} -ne 0 ]; then\n print_error \"Missing required tools: ${missing_tools[*]}\"\n echo -e \"\\nInstallation instructions:\"\n for tool in \"${missing_tools[@]}\"; do\n case $tool in\n terragrunt)\n echo \" - terragrunt: https://terragrunt.gruntwork.io/docs/getting-started/install/\"\n ;;\n \"terraform or opentofu\")\n echo \" - terraform: https://developer.hashicorp.com/terraform/downloads\"\n echo \" - opentofu: https://opentofu.org/docs/intro/install/\"\n ;;\n esac\n done\n exit 1\n fi\n}\n\n# Format check\nformat_check() {\n print_header \"HCL Format Check\"\n\n cd \"$TARGET_DIR\"\n\n # Try new command first, fall back to old if needed\n if terragrunt hcl fmt --check 2>/dev/null; then\n print_success \"All HCL files are properly formatted\"\n elif terragrunt hcl format --check 2>/dev/null; then\n print_success \"All HCL files are properly formatted\"\n else\n print_error \"HCL files are not properly formatted\"\n echo -e \"\\nRun 'terragrunt hcl fmt' to fix formatting issues\"\n return 1\n fi\n}\n\n# Validate HCL inputs (new in Terragrunt 0.93+)\nvalidate_inputs() {\n print_header \"HCL Input Validation\"\n\n cd \"$TARGET_DIR\"\n\n local mode\n mode=$(detect_target_mode)\n local -a validate_cmd=()\n\n if [[ \"$mode\" == \"multi\" ]]; then\n validate_cmd=(terragrunt hcl validate --inputs --all)\n print_info \"Running input validation across all units...\"\n elif [[ \"$mode\" == \"single\" ]]; then\n validate_cmd=(terragrunt hcl validate --inputs)\n print_info \"Running input validation on single unit...\"\n elif [[ \"$mode\" == \"root-only\" ]]; then\n print_warning \"No terragrunt.hcl in current directory for input validation\"\n print_info \"Root-only directory detected (root.hcl). Run this script in a unit directory or keep multi-unit layout.\"\n return 0\n else\n print_warning \"No terragrunt.hcl files found for input validation\"\n return 0\n fi\n\n # Run the validation command\n local output\n if output=$(\"${validate_cmd[@]}\" 2>&1); then\n print_success \"All inputs validated successfully\"\n else\n local exit_code=$?\n # Check if it's a \"command not found\" error (127) or actual validation failure\n if [[ $exit_code -eq 127 ]]; then\n print_warning \"Input validation command not available\"\n print_info \"This feature requires Terragrunt 0.93+\"\n elif echo \"$output\" | grep -q \"unknown command\\|unknown flag\"; then\n print_warning \"Input validation not supported in this Terragrunt version\"\n print_info \"This feature requires Terragrunt 0.93+\"\n else\n # Show the actual error for debugging\n echo \"$output\" | head -20\n print_warning \"Input validation completed with warnings or errors\"\n # Don't fail the entire validation for input warnings\n fi\n fi\n}\n\n# Validate Terragrunt configuration syntax\n# Uses 'terragrunt hcl validate' which checks HCL structure without requiring\n# Terraform providers or remote credentials. This is distinct from format_check()\n# (whitespace/indentation) and validate_inputs() (variable alignment).\nvalidate_terragrunt() {\n print_header \"Terragrunt Configuration Check\"\n\n cd \"$TARGET_DIR\"\n\n # Check if .hcl files exist (exclude cache dirs to avoid false positives)\n if ! find . -name \"*.hcl\" -type f ! -path \"*/.terragrunt-cache/*\" | grep -q .; then\n print_error \"No .hcl files found in $TARGET_DIR\"\n return 1\n fi\n\n local mode\n mode=$(detect_target_mode)\n local -a validate_cmd=(terragrunt hcl validate)\n\n case \"$mode\" in\n multi)\n validate_cmd+=(--all)\n print_info \"Multi-unit directory detected, validating all units...\"\n ;;\n single)\n print_info \"Single-unit directory detected, validating current unit...\"\n ;;\n root-only)\n print_warning \"No terragrunt.hcl in current directory for syntax validation\"\n print_info \"Root-only directory detected (root.hcl). Run this script in a unit directory or keep multi-unit layout.\"\n return 0\n ;;\n *)\n print_error \"No Terragrunt configuration files found in $TARGET_DIR\"\n return 1\n ;;\n esac\n\n local output\n if output=$(\"${validate_cmd[@]}\" 2>&1); then\n print_success \"Terragrunt HCL syntax is valid\"\n else\n local hcl_exit=$?\n\n # Compatibility fallback for Terragrunt variants where hcl validate\n # exists but does not accept --all.\n if [[ \"$mode\" == \"multi\" ]] && echo \"$output\" | grep -qi \"unknown flag.*--all\"; then\n print_warning \"Terragrunt does not support 'hcl validate --all'; falling back to single-command validation\"\n if output=$(terragrunt hcl validate 2>&1); then\n print_success \"Terragrunt HCL syntax is valid (fallback path)\"\n return 0\n fi\n hcl_exit=$?\n fi\n\n # Command not found (127) or unknown command means pre-0.93 Terragrunt.\n # Fall back to format check as a best-effort proxy — it at least catches\n # structural HCL errors even though it is not a pure syntax validator.\n if [[ $hcl_exit -eq 127 ]] || echo \"$output\" | grep -q \"unknown command\"; then\n print_warning \"terragrunt hcl validate not available (requires 0.93+), using format check as proxy\"\n if terragrunt hcl fmt --check > /dev/null 2>&1 || terragrunt hcl format --check > /dev/null 2>&1; then\n print_success \"Terragrunt configuration syntax appears valid (format check passed)\"\n else\n print_warning \"Configuration files may have formatting or syntax issues\"\n fi\n else\n print_error \"Terragrunt HCL syntax validation failed\"\n echo \"$output\" | head -20\n return 1\n fi\n fi\n}\n\n# Validate Terraform configuration\nvalidate_terraform() {\n print_header \"Terraform Validation\"\n\n cd \"$TARGET_DIR\"\n\n local mode\n mode=$(detect_target_mode)\n\n # For multi-unit directories, run init+validate across all units.\n if [[ \"$mode\" == \"multi\" ]]; then\n print_info \"Multi-unit directory detected, using 'run --all validate'\"\n\n if [[ \"$SKIP_INIT\" != \"true\" ]]; then\n local -a multi_init_cmd=(terragrunt)\n if [[ -n \"$STRICT_FLAG\" ]]; then\n multi_init_cmd+=(\"$STRICT_FLAG\")\n fi\n multi_init_cmd+=(run --all init)\n\n if [[ \"$SKIP_BACKEND_INIT\" == \"true\" ]]; then\n multi_init_cmd+=(\"-backend=false\")\n print_info \"Backend initialization disabled for multi-unit init\"\n fi\n\n if ! \"${multi_init_cmd[@]}\" 2>&1; then\n print_error \"Terraform initialization failed across one or more units\"\n return 1\n fi\n else\n print_info \"Skipping terraform init step (SKIP_INIT=true)\"\n fi\n\n local -a multi_validate_cmd=(terragrunt)\n if [[ -n \"$STRICT_FLAG\" ]]; then\n multi_validate_cmd+=(\"$STRICT_FLAG\")\n fi\n multi_validate_cmd+=(run --all validate)\n\n if \"${multi_validate_cmd[@]}\" 2>&1; then\n print_success \"Terraform configuration is valid across all units\"\n else\n print_error \"Terraform validation failed across one or more units\"\n return 1\n fi\n return 0\n fi\n\n # For single unit directories\n if [[ \"$mode\" == \"single\" ]]; then\n # Initialize if needed unless explicitly skipped.\n if [[ \"$SKIP_INIT\" != \"true\" ]] && [ ! -d \".terraform\" ] && [ ! -d \".terragrunt-cache\" ]; then\n echo \"Initializing Terraform...\"\n local -a single_init_cmd=(terragrunt)\n if [[ -n \"$STRICT_FLAG\" ]]; then\n single_init_cmd+=(\"$STRICT_FLAG\")\n fi\n single_init_cmd+=(init)\n if [[ \"$SKIP_BACKEND_INIT\" == \"true\" ]]; then\n single_init_cmd+=(\"-backend=false\")\n print_info \"Backend initialization disabled for single-unit init\"\n fi\n\n if ! \"${single_init_cmd[@]}\" 2>&1; then\n print_error \"Terraform initialization failed\"\n return 1\n fi\n elif [[ \"$SKIP_INIT\" == \"true\" ]]; then\n print_info \"Skipping terraform init step (SKIP_INIT=true)\"\n fi\n\n local -a single_validate_cmd=(terragrunt)\n if [[ -n \"$STRICT_FLAG\" ]]; then\n single_validate_cmd+=(\"$STRICT_FLAG\")\n fi\n single_validate_cmd+=(validate)\n\n if \"${single_validate_cmd[@]}\" 2>&1; then\n print_success \"Terraform configuration is valid\"\n else\n print_error \"Terraform validation failed\"\n return 1\n fi\n elif [[ \"$mode\" == \"root-only\" ]]; then\n print_warning \"No terragrunt.hcl found for Terraform validation\"\n print_info \"Root-only directory detected (root.hcl). Run this script from a unit directory or a multi-unit parent.\"\n else\n print_error \"No Terragrunt configuration files found in $TARGET_DIR\"\n return 1\n fi\n}\n\n# Run tflint\nrun_tflint() {\n print_header \"TFLint Analysis\"\n\n cd \"$TARGET_DIR\"\n\n # Initialize tflint if .tflint.hcl exists\n if [ -f \".tflint.hcl\" ]; then\n tflint --init 2>/dev/null || true\n fi\n\n if tflint --recursive 2>&1; then\n print_success \"No linting issues found\"\n else\n print_error \"Linting issues detected\"\n return 1\n fi\n}\n\n# Run security scan with the available scanner\nrun_security_scan() {\n print_header \"Security Scan ($SECURITY_SCANNER)\"\n\n cd \"$TARGET_DIR\"\n\n case \"$SECURITY_SCANNER\" in\n trivy)\n print_info \"Using Trivy (recommended) for security scanning\"\n if trivy config . --severity HIGH,CRITICAL --exit-code 1 2>&1; then\n print_success \"No critical security issues found\"\n else\n if [[ \"$SOFT_FAIL_SECURITY\" == \"true\" ]]; then\n print_warning \"Security issues detected but not failing build (SOFT_FAIL_SECURITY=true)\"\n else\n print_error \"Security issues detected\"\n return 1\n fi\n fi\n ;;\n checkov)\n print_info \"Using Checkov for security scanning\"\n if checkov -d . --framework terraform 2>&1; then\n print_success \"No critical security issues found\"\n else\n if [[ \"$SOFT_FAIL_SECURITY\" == \"true\" ]]; then\n print_warning \"Security issues detected but not failing build (SOFT_FAIL_SECURITY=true)\"\n else\n print_error \"Security issues detected\"\n return 1\n fi\n fi\n ;;\n tfsec)\n print_warning \"Using tfsec (deprecated) - consider migrating to Trivy\"\n print_info \"Migration guide: https://github.com/aquasecurity/tfsec/blob/master/tfsec-to-trivy-migration-guide.md\"\n if tfsec . 2>&1; then\n print_success \"No critical security issues found\"\n else\n if [[ \"$SOFT_FAIL_SECURITY\" == \"true\" ]]; then\n print_warning \"Security issues detected but not failing build (SOFT_FAIL_SECURITY=true)\"\n else\n print_error \"Security issues detected\"\n return 1\n fi\n fi\n ;;\n *)\n print_warning \"Unknown security scanner: $SECURITY_SCANNER\"\n return 1\n ;;\n esac\n}\n\n# Generate and validate dependency graph\nvalidate_dependencies() {\n print_header \"Dependency Graph Validation\"\n\n cd \"$TARGET_DIR\"\n\n # Check if dependencies are properly configured\n if find . -name \"*.hcl\" -type f ! -path \"*/.terragrunt-cache/*\" -exec grep -l \"dependency\" {} \\; | grep -q .; then\n print_success \"Dependency blocks found in configuration\"\n\n # Try to generate DAG graph (new in 0.93+)\n if terragrunt dag graph > /dev/null 2>&1; then\n print_success \"Dependency graph is valid (no cycles detected)\"\n else\n print_info \"Could not generate dependency graph\"\n print_info \"Use 'terragrunt run --all plan' to validate dependency resolution\"\n fi\n else\n print_success \"No dependencies to validate\"\n fi\n}\n\n# Dry-run plan\nrun_plan() {\n print_header \"Terragrunt Plan (Dry-Run)\"\n\n cd \"$TARGET_DIR\"\n\n if [[ -n \"$STRICT_FLAG\" ]]; then\n print_info \"Running with strict mode enabled\"\n fi\n\n local mode\n mode=$(detect_target_mode)\n\n if [[ \"$mode\" == \"multi\" ]]; then\n echo \"Running terragrunt run --all plan...\"\n local -a plan_cmd=(terragrunt)\n if [[ -n \"$STRICT_FLAG\" ]]; then\n plan_cmd+=(\"$STRICT_FLAG\")\n fi\n plan_cmd+=(run --all plan)\n\n if \"${plan_cmd[@]}\" 2>&1; then\n print_success \"Plan generated successfully across all units\"\n else\n print_error \"Plan generation failed across one or more units\"\n return 1\n fi\n elif [[ \"$mode\" == \"single\" ]]; then\n echo \"Running terragrunt plan...\"\n local -a plan_cmd=(terragrunt)\n if [[ -n \"$STRICT_FLAG\" ]]; then\n plan_cmd+=(\"$STRICT_FLAG\")\n fi\n plan_cmd+=(plan -out=tfplan)\n\n if \"${plan_cmd[@]}\" 2>&1; then\n print_success \"Plan generated successfully\"\n echo -e \"\\nTo review the plan, run:\"\n echo \" terragrunt show tfplan\"\n else\n print_error \"Plan generation failed\"\n return 1\n fi\n else\n print_warning \"No unit terragrunt.hcl found for plan step\"\n print_info \"Skipping plan in non-unit directory\"\n fi\n}\n\n# Main execution\nmain() {\n echo -e \"${BLUE}╔═══════════════════════════════════════╗${NC}\"\n echo -e \"${BLUE}║ Terragrunt Validation Suite ║${NC}\"\n echo -e \"${BLUE}║ (Designed for Terragrunt 0.93+) ║${NC}\"\n echo -e \"${BLUE}╚═══════════════════════════════════════╝${NC}\"\n\n if [[ -n \"$STRICT_FLAG\" ]]; then\n print_info \"Strict mode enabled - deprecated features will cause errors\"\n fi\n\n if [[ ! -d \"$TARGET_DIR\" ]]; then\n print_error \"Target directory does not exist: $TARGET_DIR\"\n exit 1\n fi\n\n check_dependencies\n\n local exit_code=0\n\n # Run all checks\n format_check || exit_code=$?\n\n if [[ \"$SKIP_INPUT_VALIDATION\" != \"true\" ]]; then\n validate_inputs || true # Don't fail on input validation (may not be available)\n fi\n\n validate_terragrunt || exit_code=$?\n validate_terraform || exit_code=$?\n\n if [[ \"$SKIP_LINT\" != \"true\" ]]; then\n run_tflint || exit_code=$?\n fi\n\n if [[ \"$SKIP_SECURITY\" != \"true\" ]]; then\n run_security_scan || exit_code=$?\n fi\n\n validate_dependencies || exit_code=$?\n\n if [[ \"$SKIP_PLAN\" != \"true\" ]]; then\n run_plan || exit_code=$?\n fi\n\n # Summary\n print_header \"Validation Summary\"\n if [ $exit_code -eq 0 ]; then\n print_success \"All validation checks passed!\"\n else\n print_error \"Some validation checks failed. Please review the output above.\"\n fi\n\n exit $exit_code\n}\n\n# Show usage if --help is passed\nif [[ \"${1:-}\" == \"--help\" || \"${1:-}\" == \"-h\" ]]; then\n echo \"Usage: $0 [TARGET_DIR]\"\n echo \"\"\n echo \"Validates Terragrunt configurations with comprehensive checks.\"\n echo \"Designed for Terragrunt 0.93+ with the new CLI redesign.\"\n echo \"\"\n echo \"Options:\"\n echo \" TARGET_DIR Directory containing Terragrunt files (default: current directory)\"\n echo \"\"\n echo \"Environment Variables:\"\n echo \" SKIP_PLAN=true Skip the terragrunt plan step\"\n echo \" SKIP_SECURITY=true Skip security scanning\"\n echo \" SKIP_LINT=true Skip linting with tflint\"\n echo \" SKIP_INPUT_VALIDATION=true Skip HCL input validation\"\n echo \" SKIP_INIT=true Skip terraform init step before validate\"\n echo \" SKIP_BACKEND_INIT=true Run terraform init with -backend=false\"\n echo \" SOFT_FAIL_SECURITY=true Do not fail on scanner findings\"\n echo \" SECURITY_SCANNER=X Force specific scanner: trivy, checkov, tfsec, or auto (default)\"\n echo \" TG_STRICT_MODE=true Enable Terragrunt strict mode (errors on deprecated features)\"\n echo \"\"\n echo \"Security Scanners (in order of preference):\"\n echo \" trivy - Recommended, actively maintained (replaces tfsec)\"\n echo \" checkov - Alternative with 750+ built-in policies\"\n echo \" tfsec - Legacy, deprecated (merged into Trivy)\"\n echo \"\"\n echo \"Examples:\"\n echo \" $0 # Validate current directory\"\n echo \" $0 ./infrastructure # Validate specific directory\"\n echo \" SKIP_PLAN=true $0 # Skip plan generation\"\n echo \" SECURITY_SCANNER=trivy $0 # Force Trivy for security\"\n echo \" SKIP_BACKEND_INIT=true $0 # Avoid remote backend auth during init\"\n echo \" TG_STRICT_MODE=true $0 # Enable strict mode\"\n exit 0\nfi\n\nmain\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":23574,"content_sha256":"c64a12ec26eb68ac0d053a29257f6f901a353ad48b16c59cf54c677e0adeb864"},{"filename":"test/infrastructure/common.hcl","content":"# Common variables shared across all environments\n\nlocals {\n # AWS Region\n region = \"us-east-1\"\n\n # Availability zones\n availability_zones = [\"us-east-1a\", \"us-east-1b\", \"us-east-1c\"]\n\n # VPC CIDR blocks per environment\n vpc_cidrs = {\n dev = \"10.0.0.0/16\"\n staging = \"10.1.0.0/16\"\n prod = \"10.2.0.0/16\"\n }\n\n # Common resource naming convention\n name_prefix = \"myapp\"\n\n # Backup retention periods\n backup_retention = {\n dev = 7\n staging = 14\n prod = 30\n }\n\n # Instance sizes per environment\n instance_sizes = {\n dev = {\n database = \"db.t3.micro\"\n cache = \"cache.t3.micro\"\n app = \"t3.small\"\n }\n staging = {\n database = \"db.t3.small\"\n cache = \"cache.t3.small\"\n app = \"t3.medium\"\n }\n prod = {\n database = \"db.r6g.large\"\n cache = \"cache.r6g.large\"\n app = \"t3.large\"\n }\n }\n\n # Multi-AZ configuration\n multi_az = {\n dev = false\n staging = false\n prod = true\n }\n\n # High availability configuration\n ha_config = {\n dev = {\n min_size = 1\n max_size = 2\n desired_size = 1\n }\n staging = {\n min_size = 1\n max_size = 3\n desired_size = 2\n }\n prod = {\n min_size = 2\n max_size = 10\n desired_size = 3\n }\n }\n}\n","content_type":"text/plain; charset=utf-8","language":"hcl","size":1333,"content_sha256":"740d078703997523dc59110e1ba04d2eca6c38dc3bc1b2fc77ba09d4220f6e7a"},{"filename":"test/infrastructure/dev/app/terragrunt.hcl","content":"# Application ECS Configuration for Development\n\ninclude \"root\" {\n path = find_in_parent_folders(\"root.hcl\")\n}\n\nterraform {\n source = \"tfr:///terraform-aws-modules/ecs/aws?version=5.7.0\"\n}\n\n# Dependencies\ndependency \"vpc\" {\n config_path = \"../vpc\"\n\n mock_outputs = {\n vpc_id = \"vpc-mock-123456\"\n private_subnet_ids = [\"subnet-mock-1\", \"subnet-mock-2\"]\n }\n\n mock_outputs_allowed_terraform_commands = [\"validate\", \"plan\", \"init\"]\n}\n\ndependency \"database\" {\n config_path = \"../database\"\n\n mock_outputs = {\n db_instance_endpoint = \"mock-db-endpoint.rds.amazonaws.com:5432\"\n db_instance_name = \"myappdb\"\n }\n\n mock_outputs_allowed_terraform_commands = [\"validate\", \"plan\", \"init\"]\n}\n\ndependency \"cache\" {\n config_path = \"../cache\"\n\n mock_outputs = {\n endpoint = \"mock-redis-endpoint.cache.amazonaws.com:6379\"\n }\n\n mock_outputs_allowed_terraform_commands = [\"validate\", \"plan\", \"init\"]\n}\n\nlocals {\n common = read_terragrunt_config(find_in_parent_folders(\"common.hcl\"))\n}\n\ninputs = {\n cluster_name = \"${local.common.locals.name_prefix}-dev-cluster\"\n\n cluster_configuration = {\n execute_command_configuration = {\n logging = \"OVERRIDE\"\n log_configuration = {\n cloud_watch_log_group_name = \"/aws/ecs/${local.common.locals.name_prefix}-dev\"\n }\n }\n }\n\n # Fargate capacity providers\n fargate_capacity_providers = {\n FARGATE = {\n default_capacity_provider_strategy = {\n weight = 50\n base = 20\n }\n }\n FARGATE_SPOT = {\n default_capacity_provider_strategy = {\n weight = 50\n }\n }\n }\n\n # Service configuration\n services = {\n myapp = {\n cpu = 512\n memory = 1024\n\n container_definitions = {\n app = {\n cpu = 512\n memory = 1024\n essential = true\n image = \"nginx:latest\"\n\n port_mappings = [\n {\n name = \"http\"\n containerPort = 80\n hostPort = 80\n protocol = \"tcp\"\n }\n ]\n\n environment = [\n {\n name = \"DATABASE_ENDPOINT\"\n value = dependency.database.outputs.db_instance_endpoint\n },\n {\n name = \"REDIS_ENDPOINT\"\n value = dependency.cache.outputs.endpoint\n },\n {\n name = \"ENVIRONMENT\"\n value = \"dev\"\n }\n ]\n\n readonly_root_filesystem = false\n }\n }\n\n subnet_ids = dependency.vpc.outputs.private_subnet_ids\n\n security_group_rules = {\n ingress_http = {\n type = \"ingress\"\n from_port = 80\n to_port = 80\n protocol = \"tcp\"\n cidr_blocks = [\"10.0.0.0/16\"]\n }\n }\n }\n }\n\n tags = {\n Module = \"app\"\n }\n}\n","content_type":"text/plain; charset=utf-8","language":"hcl","size":2869,"content_sha256":"efcd26c3fd6d58ff48e392b43017b20090c6a93595885d01f7b6316f640a1138"},{"filename":"test/infrastructure/dev/cache/terragrunt.hcl","content":"# ElastiCache Redis Configuration for Development\n\ninclude \"root\" {\n path = find_in_parent_folders(\"root.hcl\")\n}\n\nterraform {\n # Using a Git module source to test custom module detection\n source = \"git::https://github.com/cloudposse/terraform-aws-elasticache-redis.git?ref=0.52.0\"\n}\n\n# Dependency on VPC\ndependency \"vpc\" {\n config_path = \"../vpc\"\n\n mock_outputs = {\n vpc_id = \"vpc-mock-123456\"\n private_subnet_ids = [\"subnet-mock-1\", \"subnet-mock-2\", \"subnet-mock-3\"]\n }\n\n mock_outputs_allowed_terraform_commands = [\"validate\", \"plan\", \"init\"]\n}\n\nlocals {\n common = read_terragrunt_config(find_in_parent_folders(\"common.hcl\"))\n}\n\ninputs = {\n name = \"${local.common.locals.name_prefix}-dev-redis\"\n namespace = \"myapp\"\n stage = \"dev\"\n\n vpc_id = dependency.vpc.outputs.vpc_id\n subnets = dependency.vpc.outputs.private_subnet_ids\n\n cluster_size = 1\n instance_type = \"cache.t3.micro\"\n engine_version = \"7.0\"\n family = \"redis7\"\n at_rest_encryption_enabled = true\n transit_encryption_enabled = true\n\n automatic_failover_enabled = false\n multi_az_enabled = false\n\n parameter = [\n {\n name = \"maxmemory-policy\"\n value = \"allkeys-lru\"\n }\n ]\n\n tags = {\n Module = \"cache\"\n }\n}\n","content_type":"text/plain; charset=utf-8","language":"hcl","size":1306,"content_sha256":"128e523461c5ebe1fc620fb9e08de3dd1794099c7dcf76a8812e4c7219fd829c"},{"filename":"test/infrastructure/dev/database/terragrunt.hcl","content":"# RDS Database Configuration for Development\n\ninclude \"root\" {\n path = find_in_parent_folders(\"root.hcl\")\n}\n\nterraform {\n source = \"tfr:///terraform-aws-modules/rds/aws?version=6.3.0\"\n}\n\n# Dependency on VPC\ndependency \"vpc\" {\n config_path = \"../vpc\"\n\n mock_outputs = {\n vpc_id = \"vpc-mock-123456\"\n database_subnet_ids = [\"subnet-mock-1\", \"subnet-mock-2\", \"subnet-mock-3\"]\n database_subnet_group_name = \"mock-subnet-group\"\n }\n\n mock_outputs_allowed_terraform_commands = [\"validate\", \"plan\", \"init\"]\n mock_outputs_merge_strategy_with_state = \"shallow\"\n}\n\nlocals {\n common = read_terragrunt_config(find_in_parent_folders(\"common.hcl\"))\n db_name = \"myappdb\"\n}\n\ninputs = {\n identifier = \"${local.common.locals.name_prefix}-dev-db\"\n\n engine = \"postgres\"\n engine_version = \"15.4\"\n family = \"postgres15\"\n major_engine_version = \"15\"\n instance_class = local.common.locals.instance_sizes[\"dev\"][\"database\"]\n\n allocated_storage = 20\n max_allocated_storage = 100\n\n db_name = local.db_name\n username = \"dbadmin\"\n port = 5432\n\n # Use VPC outputs\n db_subnet_group_name = dependency.vpc.outputs.database_subnet_group_name\n vpc_security_group_ids = [] # Should create security group\n\n multi_az = false\n deletion_protection = false\n backup_retention_period = 7\n skip_final_snapshot = true\n\n enabled_cloudwatch_logs_exports = [\"postgresql\", \"upgrade\"]\n create_cloudwatch_log_group = true\n\n parameters = [\n {\n name = \"autovacuum\"\n value = 1\n },\n {\n name = \"client_encoding\"\n value = \"utf8\"\n }\n ]\n\n tags = {\n Module = \"database\"\n }\n}\n","content_type":"text/plain; charset=utf-8","language":"hcl","size":1699,"content_sha256":"01dd63cf6c3383448505abf2ce2e5303b89e50592b3a896e4f244cfaf0d25277"},{"filename":"test/infrastructure/dev/env.hcl","content":"# Development Environment Configuration\n\ninclude \"root\" {\n path = find_in_parent_folders(\"root.hcl\")\n}\n\nlocals {\n environment = \"dev\"\n common = read_terragrunt_config(find_in_parent_folders(\"common.hcl\"))\n\n env_tags = {\n Environment = \"development\"\n CostCenter = \"engineering\"\n }\n}\n\ninputs = {\n environment = local.environment\n vpc_cidr = local.common.locals.vpc_cidrs[local.environment]\n availability_zones = local.common.locals.availability_zones\n backup_retention = local.common.locals.backup_retention[local.environment]\n multi_az = local.common.locals.multi_az[local.environment]\n\n tags = merge(\n local.env_tags,\n {\n Environment = local.environment\n }\n )\n}\n","content_type":"text/plain; charset=utf-8","language":"hcl","size":729,"content_sha256":"442869fa302d246277cfa795e766c0e70ca3cef64554b3acebe447517ee8d4bb"},{"filename":"test/infrastructure/dev/monitoring/terragrunt.hcl","content":"# Monitoring Configuration for Development with Datadog\n\ninclude \"root\" {\n path = find_in_parent_folders(\"root.hcl\")\n}\n\nterraform {\n source = \"tfr:///terraform-aws-modules/cloudwatch/aws//modules/log-group?version=4.3.0\"\n}\n\n# Generate Datadog provider configuration (custom provider)\ngenerate \"datadog_provider\" {\n path = \"datadog_provider.tf\"\n if_exists = \"overwrite_terragrunt\"\n contents = \u003c\u003cEOF\nterraform {\n required_providers {\n datadog = {\n source = \"datadog/datadog\"\n version = \"~> 3.30.0\"\n }\n }\n}\n\nprovider \"datadog\" {\n api_key = var.datadog_api_key\n app_key = var.datadog_app_key\n}\n\nvariable \"datadog_api_key\" {\n type = string\n sensitive = true\n}\n\nvariable \"datadog_app_key\" {\n type = string\n sensitive = true\n}\nEOF\n}\n\n# Dependencies\ndependency \"app\" {\n config_path = \"../app\"\n\n mock_outputs = {\n cluster_name = \"myapp-dev-cluster\"\n }\n\n mock_outputs_allowed_terraform_commands = [\"validate\", \"plan\", \"init\"]\n}\n\nlocals {\n common = read_terragrunt_config(find_in_parent_folders(\"common.hcl\"))\n}\n\ninputs = {\n name = \"/aws/ecs/${local.common.locals.name_prefix}-dev\"\n retention_in_days = 7\n\n tags = {\n Module = \"monitoring\"\n Application = dependency.app.outputs.cluster_name\n }\n}\n\n# Datadog monitors and dashboards would be configured here as well\n# using the datadog provider resources\n","content_type":"text/plain; charset=utf-8","language":"hcl","size":1373,"content_sha256":"ca0fdf2d85f8b6f850c1186988157152ec589f166ac44327761322e36cf1e818"},{"filename":"test/infrastructure/dev/vpc/terragrunt.hcl","content":"# VPC Configuration for Development\n\ninclude \"root\" {\n path = find_in_parent_folders(\"root.hcl\")\n}\n\nterraform {\n # Using Terraform Registry module\n source = \"tfr:///terraform-aws-modules/vpc/aws?version=5.1.0\"\n}\n\nlocals {\n common = read_terragrunt_config(find_in_parent_folders(\"common.hcl\"))\n env = read_terragrunt_config(find_in_parent_folders(\"env.hcl\"))\n}\n\ninputs = {\n name = \"${local.common.locals.name_prefix}-dev-vpc\"\n cidr = \"10.0.0.0/16\"\n\n azs = [\"us-east-1a\", \"us-east-1b\", \"us-east-1c\"]\n private_subnets = [\"10.0.1.0/24\", \"10.0.2.0/24\", \"10.0.3.0/24\"]\n public_subnets = [\"10.0.101.0/24\", \"10.0.102.0/24\", \"10.0.103.0/24\"]\n database_subnets = [\"10.0.201.0/24\", \"10.0.202.0/24\", \"10.0.203.0/24\"]\n\n enable_nat_gateway = true\n single_nat_gateway = true # Cost savings for dev\n enable_dns_hostnames = true\n enable_dns_support = true\n\n # VPC Flow Logs\n enable_flow_log = true\n create_flow_log_cloudwatch_iam_role = true\n create_flow_log_cloudwatch_log_group = true\n\n tags = {\n Module = \"vpc\"\n }\n}\n","content_type":"text/plain; charset=utf-8","language":"hcl","size":1073,"content_sha256":"2089f5e17332c735a3479df246634a1fcd44a44b8bfabd29beeafd9431de4947"},{"filename":"test/infrastructure/prod/database/terragrunt.hcl","content":"# RDS Database Configuration for Production\n\ninclude \"root\" {\n path = find_in_parent_folders(\"root.hcl\")\n}\n\nterraform {\n source = \"tfr:///terraform-aws-modules/rds/aws?version=6.3.0\"\n}\n\ndependency \"vpc\" {\n config_path = \"../vpc\"\n\n mock_outputs = {\n vpc_id = \"vpc-mock-123456\"\n database_subnet_ids = [\"subnet-mock-1\", \"subnet-mock-2\", \"subnet-mock-3\"]\n database_subnet_group_name = \"mock-subnet-group\"\n }\n\n mock_outputs_allowed_terraform_commands = [\"validate\", \"plan\", \"init\"]\n mock_outputs_merge_strategy_with_state = \"shallow\"\n}\n\nlocals {\n common = read_terragrunt_config(find_in_parent_folders(\"common.hcl\"))\n db_name = \"myappdb\"\n}\n\ninputs = {\n identifier = \"${local.common.locals.name_prefix}-prod-db\"\n\n engine = \"postgres\"\n engine_version = \"15.4\"\n family = \"postgres15\"\n major_engine_version = \"15\"\n instance_class = local.common.locals.instance_sizes[\"prod\"][\"database\"]\n\n allocated_storage = 100\n max_allocated_storage = 500\n storage_encrypted = true\n\n db_name = local.db_name\n username = \"dbadmin\"\n port = 5432\n\n db_subnet_group_name = dependency.vpc.outputs.database_subnet_group_name\n vpc_security_group_ids = []\n\n # Production settings\n multi_az = true\n deletion_protection = true\n backup_retention_period = 30\n skip_final_snapshot = false\n final_snapshot_identifier = \"${local.common.locals.name_prefix}-prod-db-final-snapshot\"\n\n # Performance Insights\n performance_insights_enabled = true\n performance_insights_retention_period = 7\n enabled_cloudwatch_logs_exports = [\"postgresql\", \"upgrade\"]\n create_cloudwatch_log_group = true\n\n # Automated backups\n backup_window = \"03:00-06:00\"\n maintenance_window = \"Mon:00:00-Mon:03:00\"\n\n parameters = [\n {\n name = \"autovacuum\"\n value = 1\n },\n {\n name = \"client_encoding\"\n value = \"utf8\"\n },\n {\n name = \"log_min_duration_statement\"\n value = \"1000\"\n }\n ]\n\n tags = {\n Module = \"database\"\n Compliance = \"required\"\n }\n}\n","content_type":"text/plain; charset=utf-8","language":"hcl","size":2125,"content_sha256":"0c8f01e6fb9ba2ea39cc807c368a57f0078ed5310e8a3cf041965ff2422763b0"},{"filename":"test/infrastructure/prod/env.hcl","content":"# Production Environment Configuration\n\ninclude \"root\" {\n path = find_in_parent_folders(\"root.hcl\")\n}\n\nlocals {\n environment = \"prod\"\n common = read_terragrunt_config(find_in_parent_folders(\"common.hcl\"))\n\n env_tags = {\n Environment = \"production\"\n CostCenter = \"operations\"\n Compliance = \"required\"\n }\n}\n\ninputs = {\n environment = local.environment\n vpc_cidr = local.common.locals.vpc_cidrs[local.environment]\n availability_zones = local.common.locals.availability_zones\n backup_retention = local.common.locals.backup_retention[local.environment]\n multi_az = local.common.locals.multi_az[local.environment]\n\n # Production-specific settings\n enable_deletion_protection = true\n enable_encryption = true\n\n tags = merge(\n local.env_tags,\n {\n Environment = local.environment\n }\n )\n}\n","content_type":"text/plain; charset=utf-8","language":"hcl","size":862,"content_sha256":"8bd723ece38f9262384a42a42c1f7d2b0e9d396f70d2499de0a61eae833e05cb"},{"filename":"test/infrastructure/prod/monitoring/terragrunt.hcl","content":"# Monitoring Configuration for Production with Datadog and New Relic\n\ninclude \"root\" {\n path = find_in_parent_folders(\"root.hcl\")\n}\n\nterraform {\n source = \"tfr:///terraform-aws-modules/cloudwatch/aws//modules/log-group?version=4.3.0\"\n}\n\n# Generate Datadog provider configuration\ngenerate \"datadog_provider\" {\n path = \"datadog_provider.tf\"\n if_exists = \"overwrite_terragrunt\"\n contents = \u003c\u003cEOF\nterraform {\n required_providers {\n datadog = {\n source = \"datadog/datadog\"\n version = \"~> 3.30.0\"\n }\n newrelic = {\n source = \"newrelic/newrelic\"\n version = \"~> 3.25.0\"\n }\n }\n}\n\nprovider \"datadog\" {\n api_key = var.datadog_api_key\n app_key = var.datadog_app_key\n api_url = \"https://api.datadoghq.com/\"\n}\n\nprovider \"newrelic\" {\n account_id = var.newrelic_account_id\n api_key = var.newrelic_api_key\n region = \"US\"\n}\n\nvariable \"datadog_api_key\" {\n type = string\n sensitive = true\n}\n\nvariable \"datadog_app_key\" {\n type = string\n sensitive = true\n}\n\nvariable \"newrelic_account_id\" {\n type = string\n}\n\nvariable \"newrelic_api_key\" {\n type = string\n sensitive = true\n}\nEOF\n}\n\n# Dependencies\ndependency \"database\" {\n config_path = \"../database\"\n\n mock_outputs = {\n db_instance_identifier = \"myapp-prod-db\"\n db_instance_endpoint = \"mock-db-endpoint.rds.amazonaws.com:5432\"\n }\n\n mock_outputs_allowed_terraform_commands = [\"validate\", \"plan\", \"init\"]\n}\n\nlocals {\n common = read_terragrunt_config(find_in_parent_folders(\"common.hcl\"))\n}\n\ninputs = {\n name = \"/aws/ecs/${local.common.locals.name_prefix}-prod\"\n retention_in_days = 90 # Longer retention for production\n\n tags = {\n Module = \"monitoring\"\n Environment = \"production\"\n Compliance = \"required\"\n }\n}\n\n# Additional monitoring resources would be configured using datadog and newrelic providers\n# Examples:\n# - Datadog monitors for database metrics\n# - Datadog APM configuration\n# - New Relic alert policies\n# - New Relic dashboards\n","content_type":"text/plain; charset=utf-8","language":"hcl","size":1993,"content_sha256":"89f2a05045204741ed01cfbb3fddac873a977e611132dfb8123cc03e731449e1"},{"filename":"test/infrastructure/prod/vpc/terragrunt.hcl","content":"# VPC Configuration for Production\n\ninclude \"root\" {\n path = find_in_parent_folders(\"root.hcl\")\n}\n\nterraform {\n source = \"tfr:///terraform-aws-modules/vpc/aws?version=5.1.0\"\n}\n\nlocals {\n common = read_terragrunt_config(find_in_parent_folders(\"common.hcl\"))\n}\n\ninputs = {\n name = \"${local.common.locals.name_prefix}-prod-vpc\"\n cidr = \"10.2.0.0/16\"\n\n azs = [\"us-east-1a\", \"us-east-1b\", \"us-east-1c\"]\n private_subnets = [\"10.2.1.0/24\", \"10.2.2.0/24\", \"10.2.3.0/24\"]\n public_subnets = [\"10.2.101.0/24\", \"10.2.102.0/24\", \"10.2.103.0/24\"]\n database_subnets = [\"10.2.201.0/24\", \"10.2.202.0/24\", \"10.2.203.0/24\"]\n\n enable_nat_gateway = true\n single_nat_gateway = false # Multiple NAT gateways for HA\n one_nat_gateway_per_az = true # One per AZ for production\n enable_dns_hostnames = true\n enable_dns_support = true\n\n enable_flow_log = true\n create_flow_log_cloudwatch_iam_role = true\n create_flow_log_cloudwatch_log_group = true\n\n tags = {\n Module = \"vpc\"\n Compliance = \"required\"\n }\n}\n","content_type":"text/plain; charset=utf-8","language":"hcl","size":1060,"content_sha256":"679ff511a7ea489e98de94e8c32e1f0496b98c55e9adefc5874e4430763bbc4a"},{"filename":"test/infrastructure/root.hcl","content":"# Root Terragrunt Configuration\n# This file contains shared configuration for all modules\n\n# Require Terragrunt 0.93+ for new CLI features\nterragrunt_version_constraint = \">= 0.93.0\"\n\n# Require Terraform/OpenTofu 1.6+\nterraform_version_constraint = \">= 1.6.0\"\n\nlocals {\n # Automatically load account and region variables\n account_id = get_env(\"AWS_ACCOUNT_ID\", \"123456789012\")\n aws_region = \"us-east-1\"\n\n # Common tags applied to all resources\n common_tags = {\n Terraform = \"true\"\n ManagedBy = \"Terragrunt\"\n Repository = \"infrastructure\"\n Team = \"platform\"\n }\n}\n\n# Generate AWS provider configuration\n# Note: Using \"skip\" to avoid conflicts with modules that have their own provider.tf\ngenerate \"provider\" {\n path = \"provider.tf\"\n if_exists = \"skip\"\n contents = \u003c\u003cEOF\nprovider \"aws\" {\n region = \"${local.aws_region}\"\n\n default_tags {\n tags = ${jsonencode(local.common_tags)}\n }\n}\nEOF\n}\n\n# Configure Terraform version and required providers\n# Note: Using \"skip\" to avoid conflicts with registry modules that have their own versions.tf\ngenerate \"versions\" {\n path = \"versions.tf\"\n if_exists = \"skip\"\n contents = \u003c\u003cEOF\nterraform {\n required_version = \">= 1.6.0\"\n\n required_providers {\n aws = {\n source = \"hashicorp/aws\"\n version = \"~> 5.0\"\n }\n }\n}\nEOF\n}\n\n# Configure remote state\nremote_state {\n backend = \"s3\"\n\n config = {\n bucket = \"terraform-state-${local.account_id}\"\n key = \"${path_relative_to_include()}/terraform.tfstate\"\n region = local.aws_region\n encrypt = true\n dynamodb_table = \"terraform-locks\"\n\n s3_bucket_tags = {\n Name = \"Terraform State Storage\"\n }\n\n dynamodb_table_tags = {\n Name = \"Terraform Lock Table\"\n }\n }\n\n generate = {\n path = \"backend.tf\"\n if_exists = \"overwrite_terragrunt\"\n }\n}\n\n# Configure Terragrunt behavior\nterraform {\n # Log level for Terraform\n extra_arguments \"common_vars\" {\n commands = get_terraform_commands_that_need_vars()\n }\n\n # Retry on transient errors\n extra_arguments \"retry_lock\" {\n commands = get_terraform_commands_that_need_locking()\n\n arguments = [\n \"-lock-timeout=5m\"\n ]\n }\n}\n","content_type":"text/plain; charset=utf-8","language":"hcl","size":2204,"content_sha256":"f3f13eec4f07e49552e93aa77b3de4be4697dbf6d240cc4d01cc458a177ff9d2"},{"filename":"test/infrastructure/staging/env.hcl","content":"# Staging Environment Configuration\n\ninclude \"root\" {\n path = find_in_parent_folders(\"root.hcl\")\n}\n\nlocals {\n environment = \"staging\"\n common = read_terragrunt_config(find_in_parent_folders(\"common.hcl\"))\n\n env_tags = {\n Environment = \"staging\"\n CostCenter = \"engineering\"\n }\n}\n\ninputs = {\n environment = local.environment\n vpc_cidr = local.common.locals.vpc_cidrs[local.environment]\n availability_zones = local.common.locals.availability_zones\n backup_retention = local.common.locals.backup_retention[local.environment]\n multi_az = local.common.locals.multi_az[local.environment]\n\n tags = merge(\n local.env_tags,\n {\n Environment = local.environment\n }\n )\n}\n","content_type":"text/plain; charset=utf-8","language":"hcl","size":725,"content_sha256":"623fe8b438be40f72fdf270321cc130e3e158b73a17829e25a6a5c10dfd074f0"},{"filename":"test/infrastructure/staging/vpc/terragrunt.hcl","content":"# VPC Configuration for Staging\n\ninclude \"root\" {\n path = find_in_parent_folders(\"root.hcl\")\n}\n\nterraform {\n source = \"tfr:///terraform-aws-modules/vpc/aws?version=5.1.0\"\n}\n\nlocals {\n common = read_terragrunt_config(find_in_parent_folders(\"common.hcl\"))\n}\n\ninputs = {\n name = \"${local.common.locals.name_prefix}-staging-vpc\"\n cidr = \"10.1.0.0/16\"\n\n azs = [\"us-east-1a\", \"us-east-1b\", \"us-east-1c\"]\n private_subnets = [\"10.1.1.0/24\", \"10.1.2.0/24\", \"10.1.3.0/24\"]\n public_subnets = [\"10.1.101.0/24\", \"10.1.102.0/24\", \"10.1.103.0/24\"]\n database_subnets = [\"10.1.201.0/24\", \"10.1.202.0/24\", \"10.1.203.0/24\"]\n\n enable_nat_gateway = true\n single_nat_gateway = false # Multiple NAT gateways for staging\n enable_dns_hostnames = true\n enable_dns_support = true\n\n enable_flow_log = true\n create_flow_log_cloudwatch_iam_role = true\n create_flow_log_cloudwatch_log_group = true\n\n tags = {\n Module = \"vpc\"\n }\n}\n","content_type":"text/plain; charset=utf-8","language":"hcl","size":964,"content_sha256":"0bc007028702d0d150a6208133f508e0088c29a8c6734b43fb7f1e32787c511e"},{"filename":"test/test_detect_custom_resources.py","content":"#!/usr/bin/env python3\n\"\"\"Regression tests for detect_custom_resources.py.\"\"\"\n\nimport json\nimport os\nimport subprocess\nimport tempfile\nimport textwrap\nimport unittest\nfrom pathlib import Path\n\nSKILL_DIR = Path(__file__).resolve().parents[1]\nDETECTOR = SKILL_DIR / \"scripts\" / \"detect_custom_resources.py\"\nFIXTURE_DIR = SKILL_DIR / \"test\" / \"infrastructure\"\n\n\ndef run_detector(target_dir: Path) -> dict:\n result = subprocess.run(\n [\"python3\", str(DETECTOR), str(target_dir), \"--format\", \"json\"],\n check=True,\n capture_output=True,\n text=True,\n )\n return json.loads(result.stdout)\n\n\nclass DetectCustomResourcesTests(unittest.TestCase):\n def test_detects_expected_resources_and_ignores_cache_paths(self) -> None:\n report = run_detector(FIXTURE_DIR)\n\n providers = set(report[\"custom_providers\"].keys())\n self.assertIn(\"datadog/datadog\", providers)\n self.assertIn(\"newrelic/newrelic\", providers)\n\n module_sources = set(report[\"custom_modules\"].keys())\n self.assertIn(\"tfr:///terraform-aws-modules/vpc/aws\", module_sources)\n self.assertIn(\n \"git::https://github.com/cloudposse/terraform-aws-elasticache-redis.git\",\n module_sources,\n )\n self.assertNotIn(\n \"terraform-aws-modules/s3-bucket/aws\",\n module_sources,\n \"cache-derived modules should not be detected\",\n )\n\n all_module_files = [\n item[\"file\"]\n for modules in report[\"custom_modules\"].values()\n for item in modules\n ]\n self.assertFalse(any(\".terragrunt-cache\" in path for path in all_module_files))\n\n def test_handles_required_provider_without_version(self) -> None:\n with tempfile.TemporaryDirectory() as tmp_dir:\n fixture = Path(tmp_dir) / \"main.tf\"\n fixture.write_text(\n textwrap.dedent(\n \"\"\"\n terraform {\n required_providers {\n custom = {\n source = \"company/custom\"\n }\n }\n }\n \"\"\"\n ).strip()\n + \"\\n\",\n encoding=\"utf-8\",\n )\n\n report = run_detector(Path(tmp_dir))\n self.assertIn(\"company/custom\", report[\"custom_providers\"])\n self.assertEqual(report[\"custom_providers\"][\"company/custom\"], [\"unspecified\"])\n\n def test_parses_generate_blocks_with_custom_heredoc_delimiter(self) -> None:\n with tempfile.TemporaryDirectory() as tmp_dir:\n fixture = Path(tmp_dir) / \"terragrunt.hcl\"\n fixture.write_text(\n textwrap.dedent(\n \"\"\"\n generate \"provider\" {\n path = \"provider.tf\"\n if_exists = \"overwrite_terragrunt\"\n contents = \u003c\u003cEOT\n terraform {\n required_providers {\n datadog = {\n source = \"datadog/datadog\"\n version = \"~> 3.30.0\"\n }\n }\n }\n EOT\n }\n \"\"\"\n ).strip()\n + \"\\n\",\n encoding=\"utf-8\",\n )\n\n report = run_detector(Path(tmp_dir))\n self.assertIn(\"datadog/datadog\", report[\"custom_providers\"])\n self.assertIn(\"~> 3.30.0\", report[\"custom_providers\"][\"datadog/datadog\"])\n\n def test_cache_directories_are_excluded(self) -> None:\n \"\"\"Cache exclusion must be verified with a real .terragrunt-cache directory.\n\n The broad fixture-level test only asserts the absence of a module that was\n never present in the first place. This test creates an actual cache directory\n containing a conflicting module and confirms it is not surfaced in results.\n \"\"\"\n with tempfile.TemporaryDirectory() as tmp_dir:\n # Real unit file that should be detected.\n real_dir = Path(tmp_dir) / \"dev\" / \"vpc\"\n real_dir.mkdir(parents=True)\n (real_dir / \"terragrunt.hcl\").write_text(\n 'terraform { source = \"tfr:///terraform-aws-modules/vpc/aws?version=5.1.0\" }\\n',\n encoding=\"utf-8\",\n )\n\n # Simulate a .terragrunt-cache entry that should be ignored.\n cache_dir = real_dir / \".terragrunt-cache\" / \"abc123\" / \"module\"\n cache_dir.mkdir(parents=True)\n (cache_dir / \"main.tf\").write_text(\n textwrap.dedent(\n \"\"\"\n module \"s3_bucket\" {\n source = \"terraform-aws-modules/s3-bucket/aws\"\n version = \"4.0.0\"\n }\n \"\"\"\n ).strip()\n + \"\\n\",\n encoding=\"utf-8\",\n )\n\n report = run_detector(Path(tmp_dir))\n module_sources = set(report[\"custom_modules\"].keys())\n\n self.assertIn(\n \"tfr:///terraform-aws-modules/vpc/aws\",\n module_sources,\n \"real module should be detected\",\n )\n self.assertNotIn(\n \"terraform-aws-modules/s3-bucket/aws\",\n module_sources,\n \"module inside .terragrunt-cache must not be surfaced\",\n )\n\n def test_module_block_with_nested_object_before_source(self) -> None:\n \"\"\"Source attribute must be found even when nested blocks appear first.\n\n The old regex (non-greedy .*?) stopped at the first closing brace it saw,\n so a source attribute placed after any nested object was silently dropped.\n The fix uses _extract_balanced_braces, matching the approach used for\n required_providers.\n \"\"\"\n with tempfile.TemporaryDirectory() as tmp_dir:\n fixture = Path(tmp_dir) / \"main.tf\"\n fixture.write_text(\n textwrap.dedent(\n \"\"\"\n module \"example\" {\n config = {\n key = \"value\"\n }\n source = \"my-org/example-module/aws\"\n version = \"1.2.0\"\n }\n \"\"\"\n ).strip()\n + \"\\n\",\n encoding=\"utf-8\",\n )\n\n report = run_detector(Path(tmp_dir))\n self.assertIn(\n \"my-org/example-module/aws\",\n report[\"custom_modules\"],\n \"source appearing after a nested object block must still be detected\",\n )\n detected = report[\"custom_modules\"][\"my-org/example-module/aws\"]\n self.assertEqual(detected[0][\"version\"], \"1.2.0\")\n\n\nif __name__ == \"__main__\":\n unittest.main()\n","content_type":"text/x-python; charset=utf-8","language":"python","size":6999,"content_sha256":"8ab4e957a9f9604e1ea33a979742b93154d1ff6bb63181763b0305923217090c"},{"filename":"test/test_validate_terragrunt.sh","content":"#!/usr/bin/env bash\nset -euo pipefail\n\nSKILL_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")/..\" && pwd)\"\nVALIDATOR=\"$SKILL_DIR/scripts/validate_terragrunt.sh\"\n\ncleanup() {\n if [[ -n \"${TMP_DIR:-}\" && -d \"$TMP_DIR\" ]]; then\n python3 - \"$TMP_DIR\" \u003c\u003c'PY'\nimport shutil\nimport sys\nfrom pathlib import Path\n\ntarget = Path(sys.argv[1])\nif target.exists():\n shutil.rmtree(target)\nPY\n fi\n}\ntrap cleanup EXIT\n\ncreate_common_stubs() {\n local bin_dir=\"$1\"\n\n cat > \"$bin_dir/terraform\" \u003c\u003c'EOF'\n#!/usr/bin/env bash\nset -euo pipefail\nif [[ \"${1:-}\" == \"version\" && \"${2:-}\" == \"-json\" ]]; then\n echo '{\"terraform_version\":\"1.6.0\"}'\n exit 0\nfi\necho \"Terraform v1.6.0\"\nEOF\n chmod +x \"$bin_dir/terraform\"\n}\n\nsetup_multi_failure_case() {\n local root_dir=\"$1\"\n local bin_dir=\"$root_dir/bin\"\n mkdir -p \"$bin_dir\" \"$root_dir/infra/dev/vpc\"\n cat > \"$root_dir/infra/dev/vpc/terragrunt.hcl\" \u003c\u003c'EOF'\nlocals {}\nEOF\n\n cat > \"$bin_dir/terragrunt\" \u003c\u003c'EOF'\n#!/usr/bin/env bash\nset -euo pipefail\nif [[ \"${1:-}\" == \"--strict-mode\" ]]; then\n shift\nfi\n\ncase \"${1:-}\" in\n --version)\n echo \"terragrunt version v0.99.4\"\n exit 0\n ;;\n hcl)\n exit 0\n ;;\n dag)\n exit 0\n ;;\n run)\n if [[ \"${2:-}\" == \"--all\" && \"${3:-}\" == \"init\" ]]; then\n exit 0\n fi\n if [[ \"${2:-}\" == \"--all\" && \"${3:-}\" == \"validate\" ]]; then\n exit 1\n fi\n if [[ \"${2:-}\" == \"--all\" && \"${3:-}\" == \"plan\" ]]; then\n exit 0\n fi\n ;;\nesac\n\nexit 0\nEOF\n chmod +x \"$bin_dir/terragrunt\"\n\n create_common_stubs \"$bin_dir\"\n}\n\nsetup_security_failure_case() {\n local root_dir=\"$1\"\n local bin_dir=\"$root_dir/bin\"\n mkdir -p \"$bin_dir\" \"$root_dir/single\"\n cat > \"$root_dir/single/terragrunt.hcl\" \u003c\u003c'EOF'\nlocals {}\nEOF\n\n cat > \"$bin_dir/terragrunt\" \u003c\u003c'EOF'\n#!/usr/bin/env bash\nset -euo pipefail\nif [[ \"${1:-}\" == \"--strict-mode\" ]]; then\n shift\nfi\n\ncase \"${1:-}\" in\n --version)\n echo \"terragrunt version v0.99.4\"\n exit 0\n ;;\n hcl)\n exit 0\n ;;\n init)\n exit 0\n ;;\n validate)\n exit 0\n ;;\n dag)\n exit 0\n ;;\n plan)\n exit 0\n ;;\nesac\n\nexit 0\nEOF\n chmod +x \"$bin_dir/terragrunt\"\n\n cat > \"$bin_dir/trivy\" \u003c\u003c'EOF'\n#!/usr/bin/env bash\nset -euo pipefail\n# Simulate HIGH/CRITICAL findings.\nexit 1\nEOF\n chmod +x \"$bin_dir/trivy\"\n\n create_common_stubs \"$bin_dir\"\n}\n\n# Test 1: multi-unit terraform validation failure must return non-zero.\nTMP_DIR=\"$(mktemp -d)\"\nsetup_multi_failure_case \"$TMP_DIR\"\nif PATH=\"$TMP_DIR/bin:$PATH\" SKIP_PLAN=true SKIP_SECURITY=true SKIP_LINT=true SKIP_INPUT_VALIDATION=true bash \"$VALIDATOR\" \"$TMP_DIR/infra\" >/dev/null 2>&1; then\n echo \"FAIL: expected non-zero exit for multi-unit validate failure\"\n exit 1\nfi\ncleanup\n\n# Test 2: security findings must fail by default.\nTMP_DIR=\"$(mktemp -d)\"\nsetup_security_failure_case \"$TMP_DIR\"\nif PATH=\"$TMP_DIR/bin:$PATH\" SKIP_PLAN=true SKIP_LINT=true SKIP_INPUT_VALIDATION=true SECURITY_SCANNER=trivy bash \"$VALIDATOR\" \"$TMP_DIR/single\" >/dev/null 2>&1; then\n echo \"FAIL: expected non-zero exit on security findings\"\n exit 1\nfi\ncleanup\n\n# Test 3: security findings may be soft-failed when explicitly requested.\nTMP_DIR=\"$(mktemp -d)\"\nsetup_security_failure_case \"$TMP_DIR\"\nPATH=\"$TMP_DIR/bin:$PATH\" SKIP_PLAN=true SKIP_LINT=true SKIP_INPUT_VALIDATION=true SECURITY_SCANNER=trivy SOFT_FAIL_SECURITY=true bash \"$VALIDATOR\" \"$TMP_DIR/single\" >/dev/null\ncleanup\n\n# ---------------------------------------------------------------------------\n# Helper: a multi-unit directory where every terragrunt command succeeds.\n# Used to test validate_inputs() behaviour without the noise of other failures.\n# ---------------------------------------------------------------------------\nsetup_all_pass_multi_case() {\n local root_dir=\"$1\"\n local bin_dir=\"$root_dir/bin\"\n mkdir -p \"$bin_dir\" \"$root_dir/infra/dev/vpc\"\n cat > \"$root_dir/infra/dev/vpc/terragrunt.hcl\" \u003c\u003c'EOF'\nlocals {}\nEOF\n\n cat > \"$bin_dir/terragrunt\" \u003c\u003c'STUB'\n#!/usr/bin/env bash\nset -euo pipefail\nif [[ \"${1:-}\" == \"--strict-mode\" ]]; then shift; fi\n\ncase \"${1:-}\" in\n --version)\n echo \"terragrunt version v0.99.4\"\n exit 0\n ;;\n hcl)\n # hcl fmt --check → format_check() → success\n # hcl validate → validate_terragrunt() → success\n # hcl validate --inputs [--all] → validate_inputs() → success\n exit 0\n ;;\n dag)\n exit 0\n ;;\n run)\n exit 0\n ;;\nesac\n\nexit 0\nSTUB\n chmod +x \"$bin_dir/terragrunt\"\n create_common_stubs \"$bin_dir\"\n}\n\n# ---------------------------------------------------------------------------\n# Helper: like above but hcl validate --inputs returns 1 to simulate an\n# input-alignment mismatch. hcl validate (no --inputs) still returns 0 so\n# that validate_terragrunt() passes independently.\n# ---------------------------------------------------------------------------\nsetup_input_validation_failure_case() {\n local root_dir=\"$1\"\n local bin_dir=\"$root_dir/bin\"\n mkdir -p \"$bin_dir\" \"$root_dir/infra/dev/vpc\"\n cat > \"$root_dir/infra/dev/vpc/terragrunt.hcl\" \u003c\u003c'EOF'\nlocals {}\nEOF\n\n cat > \"$bin_dir/terragrunt\" \u003c\u003c'STUB'\n#!/usr/bin/env bash\nset -euo pipefail\nif [[ \"${1:-}\" == \"--strict-mode\" ]]; then shift; fi\n\ncase \"${1:-}\" in\n --version)\n echo \"terragrunt version v0.99.4\"\n exit 0\n ;;\n hcl)\n # Distinguish: hcl validate (plain) vs hcl validate --inputs [--all]\n if [[ \"${2:-}\" == \"validate\" && \"${3:-}\" == \"--inputs\" ]]; then\n echo \"ERROR: input 'unused_var' defined in terragrunt but not declared in Terraform\"\n exit 1\n fi\n # hcl fmt --check and plain hcl validate succeed.\n exit 0\n ;;\n dag)\n exit 0\n ;;\n run)\n exit 0\n ;;\nesac\n\nexit 0\nSTUB\n chmod +x \"$bin_dir/terragrunt\"\n create_common_stubs \"$bin_dir\"\n}\n\n# ---------------------------------------------------------------------------\n# Helper: hcl validate (syntax check) returns 1 to simulate broken HCL.\n# ---------------------------------------------------------------------------\nsetup_hcl_validate_failure_case() {\n local root_dir=\"$1\"\n local bin_dir=\"$root_dir/bin\"\n mkdir -p \"$bin_dir\" \"$root_dir/single\"\n cat > \"$root_dir/single/terragrunt.hcl\" \u003c\u003c'EOF'\nlocals {}\nEOF\n\n cat > \"$bin_dir/terragrunt\" \u003c\u003c'STUB'\n#!/usr/bin/env bash\nset -euo pipefail\nif [[ \"${1:-}\" == \"--strict-mode\" ]]; then shift; fi\n\ncase \"${1:-}\" in\n --version)\n echo \"terragrunt version v0.99.4\"\n exit 0\n ;;\n hcl)\n if [[ \"${2:-}\" == \"validate\" && \"${3:-}\" != \"--inputs\" ]]; then\n # Plain hcl validate → simulate syntax error\n echo \"Error: unexpected token on line 3\"\n exit 1\n fi\n # hcl fmt --check and hcl validate --inputs succeed.\n exit 0\n ;;\n dag)\n exit 0\n ;;\n init)\n exit 0\n ;;\n validate)\n exit 0\n ;;\n plan)\n exit 0\n ;;\nesac\n\nexit 0\nSTUB\n chmod +x \"$bin_dir/terragrunt\"\n create_common_stubs \"$bin_dir\"\n}\n\n# ---------------------------------------------------------------------------\n# Helper: multi-unit root has no terragrunt.hcl, so plain `hcl validate`\n# should fail; `hcl validate --all` should pass.\n# ---------------------------------------------------------------------------\nsetup_multi_requires_all_for_hcl_validate() {\n local root_dir=\"$1\"\n local bin_dir=\"$root_dir/bin\"\n mkdir -p \"$bin_dir\" \"$root_dir/infra/dev/vpc\"\n cat > \"$root_dir/infra/dev/vpc/terragrunt.hcl\" \u003c\u003c'EOF'\nlocals {}\nEOF\n\n cat > \"$bin_dir/terragrunt\" \u003c\u003c'STUB'\n#!/usr/bin/env bash\nset -euo pipefail\nif [[ \"${1:-}\" == \"--strict-mode\" ]]; then shift; fi\n\ncase \"${1:-}\" in\n --version)\n echo \"terragrunt version v0.99.4\"\n exit 0\n ;;\n hcl)\n if [[ \"${2:-}\" == \"validate\" && \"${3:-}\" == \"--all\" ]]; then\n exit 0\n fi\n if [[ \"${2:-}\" == \"validate\" ]]; then\n echo \"no terragrunt.hcl in current directory\"\n exit 1\n fi\n exit 0\n ;;\n dag)\n exit 0\n ;;\n run)\n exit 0\n ;;\nesac\n\nexit 0\nSTUB\n chmod +x \"$bin_dir/terragrunt\"\n create_common_stubs \"$bin_dir\"\n}\n\n# ---------------------------------------------------------------------------\n# Helper: simulate Terragrunt variant where `hcl validate --all` is unsupported\n# but plain `hcl validate` succeeds.\n# ---------------------------------------------------------------------------\nsetup_multi_unknown_flag_fallback_case() {\n local root_dir=\"$1\"\n local bin_dir=\"$root_dir/bin\"\n local calls_file=\"$root_dir/terragrunt.calls\"\n mkdir -p \"$bin_dir\" \"$root_dir/infra/dev/vpc\"\n cat > \"$root_dir/infra/terragrunt.hcl\" \u003c\u003c'EOF'\nlocals {}\nEOF\n cat > \"$root_dir/infra/dev/vpc/terragrunt.hcl\" \u003c\u003c'EOF'\nlocals {}\nEOF\n : > \"$calls_file\"\n\n cat > \"$bin_dir/terragrunt\" \u003c\u003cEOF\n#!/usr/bin/env bash\nset -euo pipefail\necho \"\\$*\" >> \"$calls_file\"\nif [[ \"\\${1:-}\" == \"--strict-mode\" ]]; then shift; fi\n\ncase \"\\${1:-}\" in\n --version)\n echo \"terragrunt version v0.99.4\"\n exit 0\n ;;\n hcl)\n if [[ \"\\${2:-}\" == \"validate\" && \"\\${3:-}\" == \"--all\" ]]; then\n echo \"unknown flag: --all\" >&2\n exit 1\n fi\n if [[ \"\\${2:-}\" == \"validate\" ]]; then\n exit 0\n fi\n exit 0\n ;;\n dag)\n exit 0\n ;;\n run)\n exit 0\n ;;\nesac\n\nexit 0\nEOF\n chmod +x \"$bin_dir/terragrunt\"\n create_common_stubs \"$bin_dir\"\n}\n\n# ---------------------------------------------------------------------------\n# Helper: root-only Terragrunt layout (root.hcl only, no unit terragrunt.hcl).\n# ---------------------------------------------------------------------------\nsetup_root_only_case() {\n local root_dir=\"$1\"\n local bin_dir=\"$root_dir/bin\"\n mkdir -p \"$bin_dir\" \"$root_dir/root-only\"\n cat > \"$root_dir/root-only/root.hcl\" \u003c\u003c'EOF'\nlocals {}\nEOF\n\n cat > \"$bin_dir/terragrunt\" \u003c\u003c'STUB'\n#!/usr/bin/env bash\nset -euo pipefail\nif [[ \"${1:-}\" == \"--strict-mode\" ]]; then shift; fi\n\ncase \"${1:-}\" in\n --version)\n echo \"terragrunt version v0.99.4\"\n exit 0\n ;;\n hcl)\n exit 0\n ;;\n dag)\n exit 0\n ;;\n run)\n exit 0\n ;;\nesac\n\nexit 0\nSTUB\n chmod +x \"$bin_dir/terragrunt\"\n create_common_stubs \"$bin_dir\"\n}\n\n# Test 4: validate_inputs() runs without SKIP_INPUT_VALIDATION and succeeds.\nTMP_DIR=\"$(mktemp -d)\"\nsetup_all_pass_multi_case \"$TMP_DIR\"\nif ! PATH=\"$TMP_DIR/bin:$PATH\" SKIP_PLAN=true SKIP_SECURITY=true SKIP_LINT=true \\\n bash \"$VALIDATOR\" \"$TMP_DIR/infra\" >/dev/null 2>&1; then\n echo \"FAIL: expected zero exit when validate_inputs succeeds in multi-unit mode\"\n exit 1\nfi\ncleanup\n\n# Test 5: validate_inputs() failure must NOT cause the overall validation to fail.\nTMP_DIR=\"$(mktemp -d)\"\nsetup_input_validation_failure_case \"$TMP_DIR\"\nif ! PATH=\"$TMP_DIR/bin:$PATH\" SKIP_PLAN=true SKIP_SECURITY=true SKIP_LINT=true \\\n bash \"$VALIDATOR\" \"$TMP_DIR/infra\" >/dev/null 2>&1; then\n echo \"FAIL: validate_inputs failure should be non-fatal but caused non-zero exit\"\n exit 1\nfi\ncleanup\n\n# Test 6: validate_terragrunt() failure (hcl validate error) must propagate to\n# overall exit code. This confirms the new implementation actually fails\n# on syntax errors instead of printing a silent warning and returning 0.\nTMP_DIR=\"$(mktemp -d)\"\nsetup_hcl_validate_failure_case \"$TMP_DIR\"\nif PATH=\"$TMP_DIR/bin:$PATH\" SKIP_PLAN=true SKIP_SECURITY=true SKIP_LINT=true SKIP_INPUT_VALIDATION=true \\\n bash \"$VALIDATOR\" \"$TMP_DIR/single\" >/dev/null 2>&1; then\n echo \"FAIL: expected non-zero exit when hcl validate reports a syntax error\"\n exit 1\nfi\ncleanup\n\n# Test 7: multi-unit syntax validation must use `hcl validate --all` when the\n# root directory has no terragrunt.hcl.\nTMP_DIR=\"$(mktemp -d)\"\nsetup_multi_requires_all_for_hcl_validate \"$TMP_DIR\"\nif ! PATH=\"$TMP_DIR/bin:$PATH\" SKIP_PLAN=true SKIP_SECURITY=true SKIP_LINT=true SKIP_INPUT_VALIDATION=true \\\n bash \"$VALIDATOR\" \"$TMP_DIR/infra\" >/dev/null 2>&1; then\n echo \"FAIL: expected success when multi-unit syntax validation uses --all\"\n exit 1\nfi\ncleanup\n\n# Test 8: when `hcl validate --all` is unsupported, validator should fallback\n# to plain `hcl validate`.\nTMP_DIR=\"$(mktemp -d)\"\nsetup_multi_unknown_flag_fallback_case \"$TMP_DIR\"\nif ! PATH=\"$TMP_DIR/bin:$PATH\" SKIP_PLAN=true SKIP_SECURITY=true SKIP_LINT=true SKIP_INPUT_VALIDATION=true \\\n bash \"$VALIDATOR\" \"$TMP_DIR/infra\" >/dev/null 2>&1; then\n echo \"FAIL: expected success when --all fallback path is available\"\n exit 1\nfi\nif ! grep -q \"^hcl validate --all$\" \"$TMP_DIR/terragrunt.calls\"; then\n echo \"FAIL: expected hcl validate --all to be attempted in multi mode\"\n exit 1\nfi\nif ! grep -q \"^hcl validate$\" \"$TMP_DIR/terragrunt.calls\"; then\n echo \"FAIL: expected fallback to plain hcl validate when --all is unsupported\"\n exit 1\nfi\ncleanup\n\n# Test 9: root-only mode should not fail Terragrunt syntax validation.\nTMP_DIR=\"$(mktemp -d)\"\nsetup_root_only_case \"$TMP_DIR\"\nif ! PATH=\"$TMP_DIR/bin:$PATH\" SKIP_PLAN=true SKIP_SECURITY=true SKIP_LINT=true SKIP_INPUT_VALIDATION=true \\\n bash \"$VALIDATOR\" \"$TMP_DIR/root-only\" >/dev/null 2>&1; then\n echo \"FAIL: expected root-only mode to skip syntax/terraform checks without failing\"\n exit 1\nfi\ncleanup\n\necho \"PASS: validate_terragrunt.sh regression tests\"\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":12935,"content_sha256":"f5625d92c12308c860814258eb4a5dc331ffcb72f6930da945a87aa4b1e3cf15"}],"content_json":{"type":"doc","content":[{"type":"heading","attrs":{"level":1},"content":[{"text":"Terragrunt Validator","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Overview","type":"text"}]},{"type":"paragraph","content":[{"text":"This skill provides comprehensive validation, linting, and testing capabilities for Terragrunt configurations. Terragrunt is a thin wrapper for Terraform/OpenTofu that provides extra tools for keeping configurations DRY (Don't Repeat Yourself), working with multiple modules, and managing remote state.","type":"text"}]},{"type":"paragraph","content":[{"text":"Use this skill when:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Validating Terragrunt HCL files (*.hcl, terragrunt.hcl, terragrunt.stack.hcl)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Working with Terragrunt Stacks (unit/stack blocks, ","type":"text"},{"text":"terragrunt stack generate/run","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Performing dry-run testing with ","type":"text"},{"text":"terragrunt plan","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Linting Terragrunt/Terraform code for best practices","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Detecting and researching custom providers or modules","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Debugging Terragrunt configuration issues","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Checking dependency graphs","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Formatting HCL files","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Running security scans on infrastructure code (Trivy, Checkov)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Generating run reports and summaries","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Terragrunt Version Compatibility","type":"text"}]},{"type":"paragraph","content":[{"text":"This skill is designed for ","type":"text"},{"text":"Terragrunt 0.93+","type":"text","marks":[{"type":"strong"}]},{"text":" which includes the new CLI redesign.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"CLI Command Migration Reference","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Deprecated Command","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"New Command","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"run-all","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"run --all","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"hclfmt","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"hcl fmt","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"hclvalidate","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"hcl validate","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"validate-inputs","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"hcl validate --inputs","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"graph-dependencies","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"dag graph","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"render-json","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"render --json -w","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"terragrunt-info","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"info print","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"plan-all","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"apply-all","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"run --all plan","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"run --all apply","type":"text","marks":[{"type":"code_inline"}]}]}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Key Changes in 0.93+:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"terragrunt run --all","type":"text","marks":[{"type":"code_inline"}]},{"text":" replaces ","type":"text"},{"text":"terragrunt run-all","type":"text","marks":[{"type":"code_inline"}]},{"text":" for multi-module operations","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"terragrunt dag graph","type":"text","marks":[{"type":"code_inline"}]},{"text":" replaces ","type":"text"},{"text":"terragrunt graph-dependencies","type":"text","marks":[{"type":"code_inline"}]},{"text":" for dependency visualization","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"terragrunt hcl validate --inputs","type":"text","marks":[{"type":"code_inline"}]},{"text":" replaces ","type":"text"},{"text":"validate-inputs","type":"text","marks":[{"type":"code_inline"}]},{"text":" for input validation","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"HCL syntax validation via ","type":"text"},{"text":"terragrunt hcl fmt --check","type":"text","marks":[{"type":"code_inline"}]},{"text":" or ","type":"text"},{"text":"terragrunt hcl validate","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Full validation requires ","type":"text"},{"text":"terragrunt init && terragrunt validate","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"paragraph","content":[{"text":"If using an older Terragrunt version, some commands may need adjustment.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Core Capabilities","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"1. Comprehensive Validation Suite","type":"text"}]},{"type":"paragraph","content":[{"text":"Run the comprehensive validation script to perform all checks at once:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"bash scripts/validate_terragrunt.sh [TARGET_DIR]","type":"text"}]},{"type":"paragraph","content":[{"text":"What it validates:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"HCL formatting (","type":"text"},{"text":"terragrunt hcl fmt --check","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"HCL input validation (","type":"text"},{"text":"terragrunt hcl validate --inputs","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Terragrunt configuration syntax","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Terraform configuration validation","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Linting with tflint","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Security scanning with Trivy (or legacy tfsec)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Dependency graph validation","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Dry-run planning","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Environment variables:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"SKIP_PLAN=true","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Skip terragrunt plan step","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"SKIP_SECURITY=true","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Skip security scanning (Trivy/tfsec)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"SKIP_LINT=true","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Skip tflint linting","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"SKIP_INIT=true","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Skip ","type":"text"},{"text":"terragrunt init","type":"text","marks":[{"type":"code_inline"}]},{"text":" before validation","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"SKIP_BACKEND_INIT=true","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Run init with ","type":"text"},{"text":"-backend=false","type":"text","marks":[{"type":"code_inline"}]},{"text":" (useful in CI/offline)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"SOFT_FAIL_SECURITY=true","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Report security findings without failing","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"TG_STRICT_MODE=true","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Enable strict mode (errors on deprecated features)","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Example usage:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Full validation\nbash scripts/validate_terragrunt.sh ./infrastructure/prod\n\n# Skip plan generation (faster)\nSKIP_PLAN=true bash scripts/validate_terragrunt.sh ./infrastructure\n\n# Only validate, skip linting and security\nSKIP_LINT=true SKIP_SECURITY=true bash scripts/validate_terragrunt.sh","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"2. Custom Provider and Module Detection","type":"text"}]},{"type":"paragraph","content":[{"text":"Use the detection script to identify custom providers and modules that may require documentation lookup:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"python3 scripts/detect_custom_resources.py [DIRECTORY] [--format text|json]","type":"text"}]},{"type":"paragraph","content":[{"text":"What it detects:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Custom Terraform providers (non-HashiCorp)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Remote modules (Git, Terraform Registry, HTTP)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Provider versions","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Module versions and sources","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Output formats:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"text","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Human-readable report with search recommendations","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"json","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Machine-readable format for automation","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"When custom resources are detected:","type":"text","marks":[{"type":"strong"}]}]},{"type":"blockquote","content":[{"type":"paragraph","content":[{"text":"CRITICAL: You MUST look up documentation for EVERY detected custom resource (both providers AND modules). Do NOT skip any. This is mandatory, not optional.","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"For custom providers:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Option A - WebSearch:","type":"text","marks":[{"type":"strong"}]},{"text":" Search for provider documentation","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Query format: ","type":"text"},{"text":"\"{provider_source} terraform provider documentation version {version}\"","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Example: ","type":"text"},{"text":"\"mongodb/mongodbatlas terraform provider documentation version 1.14.0\"","type":"text","marks":[{"type":"code_inline"}]}]}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Option B - Context7 MCP (Preferred):","type":"text","marks":[{"type":"strong"}]},{"text":" Use Context7 for structured documentation lookup","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Step 1: Resolve library ID: ","type":"text"},{"text":"mcp__context7__resolve-library-id","type":"text","marks":[{"type":"code_inline"}]},{"text":" with provider name (e.g., \"datadog terraform provider\")","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Step 2: ","type":"text"},{"text":"REQUIRED","type":"text","marks":[{"type":"strong"}]},{"text":" - Fetch docs via ","type":"text"},{"text":"mcp__context7__query-docs","type":"text","marks":[{"type":"code_inline"}]},{"text":" with the resolved library ID","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Use queries like ","type":"text"},{"text":"\"authentication requirements\"","type":"text","marks":[{"type":"code_inline"}]},{"text":" and ","type":"text"},{"text":"\"configuration examples\"","type":"text","marks":[{"type":"code_inline"}]}]}]}]}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"For custom modules (EQUALLY IMPORTANT - DO NOT SKIP):","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Terraform Registry modules:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Use Context7: ","type":"text"},{"text":"mcp__context7__resolve-library-id","type":"text","marks":[{"type":"code_inline"}]},{"text":" with module name (e.g., \"terraform-aws-modules vpc\")","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Then fetch docs with ","type":"text"},{"text":"mcp__context7__query-docs","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Or visit ","type":"text"},{"text":"https://registry.terraform.io/modules/{source}/{version}","type":"text","marks":[{"type":"code_inline"}]}]}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Git modules:","type":"text","marks":[{"type":"strong"}]},{"text":" Use WebSearch with the repository URL to find README or documentation","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"HTTP modules:","type":"text","marks":[{"type":"strong"}]},{"text":" Investigate the source URL for documentation","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Pay attention to version compatibility with your Terraform/Terragrunt version","type":"text"}]}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Documentation lookup workflow (MANDATORY for ALL detected resources):","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"a) Run detect_custom_resources.py\nb) For EACH custom provider/module:\n - Note the exact version\n - Use Context7 MCP:\n 1. mcp__context7__resolve-library-id with libraryName: \"{provider/module name}\"\n 2. mcp__context7__query-docs with:\n - libraryId: \"{resolved ID}\"\n - query: \"authentication requirements\" (for auth requirements)\n 3. mcp__context7__query-docs with:\n - libraryId: \"{resolved ID}\"\n - query: \"configuration examples\" (for setup requirements)\n - OR use WebSearch with version-specific queries\n - Review documentation for:\n * Required configuration blocks\n * Authentication requirements (API keys, credentials)\n * Available resources/data sources\n * Known issues or breaking changes in the version\nc) Apply learnings to validation/troubleshooting\nd) Document findings if issues are encountered","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Example using Context7 MCP:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"# 1. Detect custom resources\npython3 scripts/detect_custom_resources.py ./infrastructure\n# Output: Provider: datadog/datadog, Version: 3.30.0\n\n# 2. Resolve library ID\nmcp__context7__resolve-library-id with libraryName: \"datadog terraform provider\"\n# Result: /datadog/terraform-provider-datadog\n\n# 3. Fetch authentication docs (REQUIRED)\nmcp__context7__query-docs with:\n libraryId: \"/datadog/terraform-provider-datadog\"\n query: \"authentication requirements\"\n\n# 4. Fetch configuration docs\nmcp__context7__query-docs with:\n libraryId: \"/datadog/terraform-provider-datadog\"\n query: \"configuration examples\"","type":"text"}]},{"type":"paragraph","content":[{"text":"Example using WebSearch:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Detect custom resources\npython3 scripts/detect_custom_resources.py ./infrastructure\n\n# Then search for documentation:\n# WebSearch: \"datadog terraform provider 3.30.0 authentication configuration\"\n# WebSearch: \"datadog terraform provider api_key app_key setup\"","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"3. Step-by-Step Validation","type":"text"}]},{"type":"paragraph","content":[{"text":"For manual or granular validation, use these individual commands:","type":"text"}]},{"type":"heading","attrs":{"level":4},"content":[{"text":"Format Validation","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"cd \u003ctarget-directory>\nterragrunt hcl fmt --check\n\n# To auto-fix formatting\nterragrunt hcl fmt","type":"text"}]},{"type":"heading","attrs":{"level":4},"content":[{"text":"Configuration Validation","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Check HCL syntax and formatting\nterragrunt hcl fmt --check\n\n# Note: In Terragrunt 0.93+, for deeper configuration validation,\n# initialize and validate (requires actual resources/credentials):\n# terragrunt init && terragrunt validate","type":"text"}]},{"type":"heading","attrs":{"level":4},"content":[{"text":"Terraform Validation","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Initialize if needed\nterragrunt init\n\n# Validate\nterragrunt validate","type":"text"}]},{"type":"heading","attrs":{"level":4},"content":[{"text":"Linting with tflint","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Initialize tflint (if .tflint.hcl exists)\ntflint --init\n\n# Run linting\ntflint --recursive","type":"text"}]},{"type":"heading","attrs":{"level":4},"content":[{"text":"Security Scanning with Trivy (Recommended)","type":"text"}]},{"type":"blockquote","content":[{"type":"paragraph","content":[{"text":"Note:","type":"text","marks":[{"type":"strong"}]},{"text":" tfsec has been merged into Trivy and is no longer actively maintained. Use Trivy for all new projects.","type":"text"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Using Trivy (recommended)\ntrivy config . --severity HIGH,CRITICAL\n\n# With tfvars file\ntrivy config --tf-vars terraform.tfvars .\n\n# Exclude downloaded modules\ntrivy config --tf-exclude-downloaded-modules .\n\n# Legacy: Using tfsec (deprecated)\ntfsec . --soft-fail","type":"text"}]},{"type":"heading","attrs":{"level":4},"content":[{"text":"Alternative: Security Scanning with Checkov","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Scan directory\ncheckov -d . --framework terraform\n\n# Scan with specific checks\ncheckov -d . --check CKV_AWS_21\n\n# Output as JSON\ncheckov -d . --output json","type":"text"}]},{"type":"heading","attrs":{"level":4},"content":[{"text":"Dependency Graph Validation","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Note: graph-dependencies command replaced with 'dag graph' in Terragrunt 0.93+\n# Validate and display dependency graph\nterragrunt dag graph\n\n# Visualize dependencies (requires graphviz)\nterragrunt dag graph | dot -Tpng > dependencies.png","type":"text"}]},{"type":"heading","attrs":{"level":4},"content":[{"text":"Dry-Run Planning","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Single module\nterragrunt plan\n\n# All modules (new syntax - Terragrunt 0.93+)\nterragrunt run --all plan\n\n# Legacy syntax (deprecated)\n# terragrunt run-all plan","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"4. Multi-Module Operations","type":"text"}]},{"type":"paragraph","content":[{"text":"For projects with multiple Terragrunt modules, use ","type":"text"},{"text":"run --all","type":"text","marks":[{"type":"code_inline"}]},{"text":" (replaces deprecated ","type":"text"},{"text":"run-all","type":"text","marks":[{"type":"code_inline"}]},{"text":"):","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Validate all modules\nterragrunt run --all validate\n\n# Plan all modules\nterragrunt run --all plan\n\n# Apply all modules\nterragrunt run --all apply\n\n# Destroy all modules\nterragrunt run --all destroy\n\n# Format all HCL files\nterragrunt hcl fmt\n\n# With parallelism\nterragrunt run --all plan --parallelism 4\n\n# With strict mode (errors on deprecated features)\nterragrunt --strict-mode run --all plan\n\n# Or via environment variable\nTG_STRICT_MODE=true terragrunt run --all plan","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"5. HCL Input Validation (New in 0.93+)","type":"text"}]},{"type":"paragraph","content":[{"text":"Validate that all required inputs are set and no unused inputs exist:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Validate inputs\nterragrunt hcl validate --inputs\n\n# Show paths of invalid files\nterragrunt hcl validate --show-config-path\n\n# Combine with run --all to exclude invalid files\nterragrunt run --all plan --queue-excludes-file \u003c(terragrunt hcl validate --show-config-path || true)","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"6. Strict Mode","type":"text"}]},{"type":"paragraph","content":[{"text":"Enable strict mode to catch deprecated features early:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Via CLI flag\nterragrunt --strict-mode run --all plan\n\n# Via environment variable (recommended for CI/CD)\nexport TG_STRICT_MODE=true\nterragrunt run --all plan\n\n# Check available strict controls\nterragrunt info strict","type":"text"}]},{"type":"paragraph","content":[{"text":"Specific Strict Controls:","type":"text","marks":[{"type":"strong"}]}]},{"type":"paragraph","content":[{"text":"For finer-grained control, use ","type":"text"},{"text":"--strict-control","type":"text","marks":[{"type":"code_inline"}]},{"text":" to enable specific controls:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Enable specific strict controls\nterragrunt run --all plan --strict-control cli-redesign --strict-control deprecated-commands\n\n# Via environment variable (comma-separated)\nTG_STRICT_CONTROL='cli-redesign,deprecated-commands' terragrunt run --all plan\n\n# Available strict controls:\n# - cli-redesign: Errors on deprecated CLI syntax\n# - deprecated-commands: Errors on deprecated commands (run-all, hclfmt, etc.)\n# - root-terragrunt-hcl: Errors when using root terragrunt.hcl (use root.hcl instead)\n# - skip-dependencies-inputs: Improves performance by not reading dependency inputs\n# - bare-include: Errors on bare include blocks (use named includes)","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"7. New CLI Commands (0.93+)","type":"text"}]},{"type":"heading","attrs":{"level":4},"content":[{"text":"Render Configuration","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Render configuration to JSON\nterragrunt render --json\n\n# Render and write to file\nterragrunt render --json --write\n\n# Output goes to terragrunt.rendered.json","type":"text"}]},{"type":"heading","attrs":{"level":4},"content":[{"text":"Info Print (replaces terragrunt-info)","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Get contextual information about current configuration\nterragrunt info print\n\n# Output includes:\n# - config_path\n# - download_dir\n# - terraform_binary\n# - working_dir","type":"text"}]},{"type":"heading","attrs":{"level":4},"content":[{"text":"Find and List Units","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Find all units/stacks in directory\nterragrunt find\n\n# Output as JSON\nterragrunt find --json\n\n# Include dependency information\nterragrunt find --json --dag\n\n# List units (simpler output)\nterragrunt list","type":"text"}]},{"type":"heading","attrs":{"level":4},"content":[{"text":"Run Summary and Reports","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Run with summary output (default in newer versions)\nterragrunt run --all plan\n\n# Disable summary output\nterragrunt run --all plan --summary-disable\n\n# Generate detailed report file\nterragrunt run --all plan --report-file=report.json\n\n# CSV format report\nterragrunt run --all plan --report-file=report.csv","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"8. Terragrunt Stacks (GA in v0.78.0+)","type":"text"}]},{"type":"paragraph","content":[{"text":"Terragrunt Stacks provide declarative infrastructure generation using ","type":"text"},{"text":"terragrunt.stack.hcl","type":"text","marks":[{"type":"code_inline"}]},{"text":" files.","type":"text"}]},{"type":"heading","attrs":{"level":4},"content":[{"text":"Stack File Structure","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"hcl"},"content":[{"text":"# terragrunt.stack.hcl\nlocals {\n environment = \"dev\"\n aws_region = \"us-east-1\"\n}\n\n# Define a unit (generates a single terragrunt.hcl)\nunit \"vpc\" {\n source = \"git::[email protected]:acme/infra-catalog.git//units/vpc?ref=v0.0.1\"\n path = \"vpc\"\n\n values = {\n environment = local.environment\n cidr = \"10.0.0.0/16\"\n }\n}\n\nunit \"database\" {\n source = \"git::[email protected]:acme/infra-catalog.git//units/database?ref=v0.0.1\"\n path = \"database\"\n\n values = {\n environment = local.environment\n vpc_path = \"../vpc\"\n }\n}\n\n# Include reusable stacks\nstack \"monitoring\" {\n source = \"git::[email protected]:acme/infra-catalog.git//stacks/monitoring?ref=v0.0.1\"\n path = \"monitoring\"\n\n values = {\n environment = local.environment\n }\n}","type":"text"}]},{"type":"heading","attrs":{"level":4},"content":[{"text":"Stack Commands","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Generate stack (creates .terragrunt-stack directory)\nterragrunt stack generate\n\n# Generate stack without validation\nterragrunt stack generate --no-stack-validate\n\n# Run command on all stack units\nterragrunt stack run plan\nterragrunt stack run apply\n\n# Clean generated stack directories\nterragrunt stack clean\n\n# Get stack outputs\nterragrunt stack output","type":"text"}]},{"type":"heading","attrs":{"level":4},"content":[{"text":"Stack Validation Control","type":"text"}]},{"type":"paragraph","content":[{"text":"Use ","type":"text"},{"text":"no_validation","type":"text","marks":[{"type":"code_inline"}]},{"text":" attribute to skip validation for specific units:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"hcl"},"content":[{"text":"unit \"experimental\" {\n source = \"git::[email protected]:acme/infra-catalog.git//units/experimental?ref=v0.0.1\"\n path = \"experimental\"\n\n # Skip validation for this unit (useful for incomplete/experimental units)\n no_validation = true\n\n values = {\n environment = local.environment\n }\n}","type":"text"}]},{"type":"heading","attrs":{"level":4},"content":[{"text":"Benefits of Stacks","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Clean working directory","type":"text","marks":[{"type":"strong"}]},{"text":": Generated code in hidden ","type":"text"},{"text":".terragrunt-stack","type":"text","marks":[{"type":"code_inline"}]},{"text":" directory","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Reusable patterns","type":"text","marks":[{"type":"strong"}]},{"text":": Define infrastructure patterns once, deploy many times","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Version pinning","type":"text","marks":[{"type":"strong"}]},{"text":": Different environments can pin different versions","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Atomic updates","type":"text","marks":[{"type":"strong"}]},{"text":": Easy rollbacks of both modules and configurations","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"9. Exec Command (Run Arbitrary Programs)","type":"text"}]},{"type":"paragraph","content":[{"text":"The ","type":"text"},{"text":"exec","type":"text","marks":[{"type":"code_inline"}]},{"text":" command allows you to run arbitrary programs against units with Terragrunt context. This is useful for integrating other tools like tflint, checkov, or AWS CLI with Terragrunt's configuration.","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Run tflint with unit context (TF_VAR_ env vars available)\nterragrunt exec -- tflint\n\n# Run checkov against specific unit\nterragrunt exec -- checkov -d .\n\n# Run AWS CLI with unit's configuration\nterragrunt exec -- aws s3 ls s3://my-bucket\n\n# Run custom scripts with Terragrunt context\nterragrunt exec -- ./scripts/validate_terragrunt.sh\n\n# Run across all units\nterragrunt run --all exec -- tflint","type":"text"}]},{"type":"paragraph","content":[{"text":"Key Features:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Terragrunt loads the inputs for the unit and makes them available as ","type":"text"},{"text":"TF_VAR_","type":"text","marks":[{"type":"code_inline"}]},{"text":" prefixed environment variables","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Works with any program that can use environment variables","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Integrates with Terragrunt's authentication context (e.g., AWS profiles)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Can be combined with ","type":"text"},{"text":"run --all","type":"text","marks":[{"type":"code_inline"}]},{"text":" for multi-unit operations","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Use Cases:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Running security scanners (checkov, trivy) with unit context","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Executing linters (tflint) per unit","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Running operational commands (AWS CLI) with correct credentials","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Custom validation scripts that need Terragrunt inputs","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"10. Feature Flags (Production Feature)","type":"text"}]},{"type":"paragraph","content":[{"text":"Terragrunt supports first-class Feature Flags for safe infrastructure changes. Feature flags allow you to integrate incomplete work without risk, decouple release from deployment, and codify IaC evolution.","type":"text"}]},{"type":"heading","attrs":{"level":4},"content":[{"text":"Defining Feature Flags","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"hcl"},"content":[{"text":"# terragrunt.hcl\nfeature \"enable_monitoring\" {\n default = false\n}\n\nfeature \"use_new_vpc\" {\n default = true\n}\n\ninputs = {\n monitoring_enabled = feature.enable_monitoring.value\n vpc_version = feature.use_new_vpc.value ? \"v2\" : \"v1\"\n}","type":"text"}]},{"type":"heading","attrs":{"level":4},"content":[{"text":"Using Feature Flags via CLI","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Enable a feature flag\nterragrunt plan --feature enable_monitoring=true\n\n# Enable multiple feature flags\nterragrunt plan --feature enable_monitoring=true --feature use_new_vpc=false\n\n# Via environment variable\nTG_FEATURE='enable_monitoring=true' terragrunt plan","type":"text"}]},{"type":"heading","attrs":{"level":4},"content":[{"text":"Feature Flags with run --all","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Apply feature flag across all units\nterragrunt run --all plan --feature enable_monitoring=true","type":"text"}]},{"type":"paragraph","content":[{"text":"Benefits:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Safe rollouts","type":"text","marks":[{"type":"strong"}]},{"text":": Test changes on subset of infrastructure","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Gradual migrations","type":"text","marks":[{"type":"strong"}]},{"text":": Enable new features incrementally","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"A/B testing","type":"text","marks":[{"type":"strong"}]},{"text":": Compare infrastructure configurations","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Emergency rollbacks","type":"text","marks":[{"type":"strong"}]},{"text":": Quickly disable problematic features","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"11. Experiments (Opt-in Unstable Features)","type":"text"}]},{"type":"paragraph","content":[{"text":"Terragrunt provides an experiments system for trying unstable features before they're GA:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Enable all experiments (not recommended for production)\nterragrunt --experiment-mode run --all plan\n\n# Enable specific experiment\nterragrunt --experiment symlinks run --all plan\n\n# Enable CAS (Content Addressable Storage) for faster cloning\nterragrunt --experiment cas run --all plan","type":"text"}]},{"type":"paragraph","content":[{"text":"Available Experiments:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"symlinks","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Support symlink resolution for Terragrunt units","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"cas","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Content Addressable Storage for faster Git/module cloning","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"filter-flag","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Advanced filtering capabilities (coming in 1.0)","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Validation Workflow","type":"text"}]},{"type":"paragraph","content":[{"text":"Follow this workflow when validating Terragrunt configurations:","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Canonical Executable Workflow (Default Path)","type":"text"}]},{"type":"paragraph","content":[{"text":"Use one executable path so docs and scripts stay aligned:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Main validation\nbash scripts/validate_terragrunt.sh \u003ctarget-directory>\n\n# Deterministic fixture tests (required after script changes)\npython3 test/test_detect_custom_resources.py\nbash test/test_validate_terragrunt.sh","type":"text"}]},{"type":"paragraph","content":[{"text":"Execution expectations:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Fixture tests should be deterministic (stable pass/fail outcomes).","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Validation/security failures must surface as non-zero exits.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 0: Read Best Practices Reference (MANDATORY FIRST STEP)","type":"text"}]},{"type":"blockquote","content":[{"type":"paragraph","content":[{"text":"You MUST read the best practices reference file BEFORE starting validation. This is not optional.","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Read the best practices reference file first\nif [ -f references/best_practices.md ]; then\n cat references/best_practices.md\nelse\n echo \"WARNING: references/best_practices.md not found; continue with built-in checklist below.\"\nfi","type":"text"}]},{"type":"paragraph","content":[{"text":"This ensures you understand the patterns, anti-patterns, and checklists you will verify.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Initial Assessment","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Understand the structure:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"tree -L 3 \u003cinfrastructure-directory>","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Identify Terragrunt files:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"find . -name \"*.hcl\" -o -name \"terragrunt.hcl\"","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Detect custom resources:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"python3 scripts/detect_custom_resources.py .","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Documentation Lookup (MANDATORY for ALL detected custom resources)","type":"text"}]},{"type":"blockquote","content":[{"type":"paragraph","content":[{"text":"CRITICAL: If ANY custom providers or modules are detected, you MUST look up documentation for EACH ONE. Do not skip any.","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"ordered_list","attrs":{"order":4,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"For EACH detected custom provider - look up documentation:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Use Context7 MCP (preferred):","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"mcp__context7__resolve-library-id","type":"text","marks":[{"type":"code_inline"}]},{"text":" with provider name","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"mcp__context7__query-docs","type":"text","marks":[{"type":"code_inline"}]},{"text":" with query: \"authentication requirements\"","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"mcp__context7__query-docs","type":"text","marks":[{"type":"code_inline"}]},{"text":" with query: \"configuration examples\"","type":"text"}]}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"OR use WebSearch: ","type":"text"},{"text":"\"{provider} terraform provider {version} documentation\"","type":"text","marks":[{"type":"code_inline"}]}]}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"For EACH detected custom module - look up documentation:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Use Context7 MCP for Terraform Registry modules:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"mcp__context7__resolve-library-id","type":"text","marks":[{"type":"code_inline"}]},{"text":" with module name (e.g., \"terraform-aws-modules vpc\")","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"mcp__context7__query-docs","type":"text","marks":[{"type":"code_inline"}]},{"text":" with relevant configuration query","type":"text"}]}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"For Git modules: Use WebSearch with repository URL","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"For HTTP modules: Investigate source URL for documentation","type":"text"}]}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Document findings for each resource:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Required configuration blocks","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Authentication requirements","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Known issues or breaking changes in the version","type":"text"}]}]}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Validation Execution","type":"text"}]},{"type":"ordered_list","attrs":{"order":7,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Run comprehensive validation:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"bash scripts/validate_terragrunt.sh \u003ctarget-directory>","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Review output for errors:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Format errors → Fix with ","type":"text"},{"text":"terragrunt hcl fmt","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Configuration errors → Check terragrunt.hcl syntax and inputs","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Terraform validation errors → Check .tf files or generated configs","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Linting issues → Review tflint output and fix","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Security issues → Review Trivy/Checkov/tfsec output and address","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Dependency errors → Check dependency blocks and paths","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Plan errors → Review Terraform configuration and provider setup","type":"text"}]}]}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Best Practices Check (REQUIRED - Must Complete All Checklists)","type":"text"}]},{"type":"blockquote","content":[{"type":"paragraph","content":[{"text":"You MUST verify each checklist item below and document the result (✅ pass or ❌ fail). Incomplete verification is not acceptable.","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"ordered_list","attrs":{"order":9,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Perform explicit best practices verification using ","type":"text","marks":[{"type":"strong"}]},{"text":"references/best_practices.md","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":":","type":"text","marks":[{"type":"strong"}]}]},{"type":"paragraph","content":[{"text":"Configuration Pattern Checklist - verify each item:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"[ ] Include blocks: Child modules use `include \"root\" { path = find_in_parent_folders(\"root.hcl\") }`\n[ ] Named includes: All include blocks have names (not bare `include {}`)\n[ ] Root file naming: Root config is named `root.hcl` (not `terragrunt.hcl`)\n[ ] Environment configs: Environment-level configs named `env.hcl` (not `terragrunt.hcl`)\n[ ] Common variables: Shared variables in `common.hcl` read via `read_terragrunt_config()`","type":"text"}]},{"type":"paragraph","content":[{"text":"Dependency Management Checklist:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"[ ] Mock outputs: ALL dependency blocks have mock_outputs for validation\n[ ] Mock allowed commands: mock_outputs_allowed_terraform_commands includes [\"validate\", \"plan\", \"init\"]\n[ ] Explicit paths: Dependency config_path uses relative paths (\"../vpc\" not absolute)\n[ ] No circular deps: Run `terragrunt dag graph` to verify no cycles","type":"text"}]},{"type":"paragraph","content":[{"text":"Security Checklist:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"[ ] State encryption: remote_state config has `encrypt = true`\n[ ] State locking: DynamoDB table configured for S3 backend\n[ ] No hardcoded credentials: Search for patterns like \"AKIA\", \"password =\", account IDs\n[ ] Sensitive variables: Passwords/keys use `sensitive = true` in variable blocks\n[ ] IAM roles: Provider uses assume_role instead of static credentials","type":"text"}]},{"type":"paragraph","content":[{"text":"DRY Principle Checklist:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"[ ] Generate blocks: Provider and backend configs use `generate` blocks\n[ ] Version constraints: terragrunt_version_constraint and terraform_version_constraint set\n[ ] Reusable locals: Common values in shared files, not duplicated\n[ ] if_exists: Generate blocks use appropriate if_exists strategy","type":"text"}]},{"type":"paragraph","content":[{"text":"Quick grep checks to run:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Check for hardcoded AWS account IDs\ngrep -r \"[0-9]\\{12\\}\" --include=\"*.hcl\" . | grep -v mock\n\n# Check for potential credentials\ngrep -ri \"password\\s*=\" --include=\"*.hcl\" .\ngrep -ri \"api_key\\s*=\" --include=\"*.hcl\" .\n\n# Check for dependencies without mock_outputs\ngrep -l \"dependency\\s\" --include=\"*.hcl\" -r . | xargs grep -L \"mock_outputs\"\n\n# Check for terragrunt.hcl files in non-module directories (anti-pattern)\nfind . -name \"terragrunt.hcl\" -not -path \"*/.terragrunt-cache/*\" | head -20","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Troubleshooting","type":"text"}]},{"type":"ordered_list","attrs":{"order":10,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Common issues and resolutions:","type":"text","marks":[{"type":"strong"}]}]}]}]},{"type":"paragraph","content":[{"text":"Issue: Module not found","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"rm -rf .terragrunt-cache\nterragrunt init","type":"text"}]},{"type":"paragraph","content":[{"text":"Issue: Provider authentication errors","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Check provider configuration in generated files","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Verify environment variables or credentials","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Review provider documentation from WebSearch","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Issue: Dependency errors","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Check dependency paths are correct","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Ensure mock_outputs are provided for validation","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Review dependency graph with ","type":"text"},{"text":"terragrunt dag graph","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"paragraph","content":[{"text":"Issue: State locking errors","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"terragrunt force-unlock \u003cLOCK_ID>","type":"text"}]},{"type":"paragraph","content":[{"text":"Issue: S3 backend ","type":"text","marks":[{"type":"strong"}]},{"text":"dynamodb_table","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" deprecation warning","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Recent Terraform versions may warn that ","type":"text"},{"text":"dynamodb_table","type":"text","marks":[{"type":"code_inline"}]},{"text":" is deprecated for S3 backends.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Prefer ","type":"text"},{"text":"use_lockfile = true","type":"text","marks":[{"type":"code_inline"}]},{"text":" in backend config when compatible with your workflow.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Keep ","type":"text"},{"text":"dynamodb_table","type":"text","marks":[{"type":"code_inline"}]},{"text":" only for legacy compatibility needs.","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Issue: Unknown provider or module parameters","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Re-run custom resource detection","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Use WebSearch to look up current documentation","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Check version compatibility","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Issue: Generate block conflicts (file already exists)","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"ERROR: The file path ./versions.tf already exists and was not generated by terragrunt.\nCan not generate terraform file: ./versions.tf already exists","type":"text"}]},{"type":"paragraph","content":[{"text":"Solution:","type":"text","marks":[{"type":"strong"}]},{"text":" This occurs when static ","type":"text"},{"text":".tf","type":"text","marks":[{"type":"code_inline"}]},{"text":" files exist that conflict with Terragrunt's ","type":"text"},{"text":"generate","type":"text","marks":[{"type":"code_inline"}]},{"text":" blocks. Either:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Remove the conflicting static files (","type":"text"},{"text":"versions.tf","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"provider.tf","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"backend.tf","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Or use ","type":"text"},{"text":"if_exists = \"skip\"","type":"text","marks":[{"type":"code_inline"}]},{"text":" in the generate block to not overwrite existing files","type":"text"}]}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Remove conflicting files\nrm -f versions.tf provider.tf backend.tf\nrm -rf .terragrunt-cache","type":"text"}]},{"type":"paragraph","content":[{"text":"Issue: Root terragrunt.hcl anti-pattern warning","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"WARN: Using `terragrunt.hcl` as the root of Terragrunt configurations is an anti-pattern","type":"text"}]},{"type":"paragraph","content":[{"text":"Solution:","type":"text","marks":[{"type":"strong"}]},{"text":" In Terragrunt 0.93+, the root configuration file should be named ","type":"text"},{"text":"root.hcl","type":"text","marks":[{"type":"code_inline"}]},{"text":" instead of ","type":"text"},{"text":"terragrunt.hcl","type":"text","marks":[{"type":"code_inline"}]},{"text":". Rename the file:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"mv terragrunt.hcl root.hcl\n# Update include blocks in child modules to reference root.hcl","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Best Practices Integration","type":"text"}]},{"type":"paragraph","content":[{"text":"Reference the comprehensive best practices guide for detailed recommendations:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Read the best practices reference\nif [ -f references/best_practices.md ]; then\n cat references/best_practices.md\nelse\n echo \"WARNING: references/best_practices.md not found; continue with checklist in this document.\"\nfi","type":"text"}]},{"type":"paragraph","content":[{"text":"Key best practices to check:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"✅ Use ","type":"text"},{"text":"include","type":"text","marks":[{"type":"code_inline"}]},{"text":" for shared configuration","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"✅ Provide mock_outputs for dependencies","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"✅ Use ","type":"text"},{"text":"generate","type":"text","marks":[{"type":"code_inline"}]},{"text":" blocks for provider config","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"✅ Enable state encryption and locking","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"✅ Use environment variables for dynamic values","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"✅ Specify version constraints","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"✅ Avoid hardcoded values","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"✅ Use meaningful directory structure","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"✅ Enable security features (encryption, IAM roles)","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"When validating, check for anti-patterns:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"❌ Hardcoded credentials or account IDs","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"❌ Missing mock outputs","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"❌ Overly deep directory nesting","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"❌ Duplicated configuration across modules","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"❌ Missing version constraints","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"❌ Unencrypted state","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Refer to ","type":"text"},{"text":"references/best_practices.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" for complete examples and detailed guidance.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Tool Requirements","type":"text"}]},{"type":"paragraph","content":[{"text":"Required:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"terragrunt (>= 0.93.0 recommended for new CLI)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"terraform or opentofu (>= 1.6.0 recommended)","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Optional but recommended:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"tflint - HCL linting","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"trivy - Security scanning (replaces tfsec)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"checkov - Alternative security scanner (750+ built-in policies)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"graphviz (dot) - Dependency visualization","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"jq - JSON parsing","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"python3 - For custom resource detection script","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Deprecated tools:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"tfsec - Merged into Trivy, no longer actively maintained","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Installation commands:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# macOS\nbrew install terragrunt terraform tflint trivy graphviz jq\n\n# Install Trivy (recommended security scanner)\nbrew install trivy\n\n# Install Checkov (alternative security scanner)\npip3 install checkov\n\n# Legacy tfsec (deprecated - use trivy instead)\n# brew install tfsec\n\n# Linux - Trivy\ncurl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin\n\n# Linux - Checkov\npip3 install checkov\n\n# Verify installations\nterragrunt --version\ntrivy --version\ncheckov --version","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Integration with Context7 MCP","type":"text"}]},{"type":"paragraph","content":[{"text":"If Context7 MCP is available, use it for provider/module documentation lookup:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Resolve library ID:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"mcp__context7__resolve-library-id with libraryName: \"mongodb/mongodbatlas\"","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Query documentation:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"mcp__context7__query-docs with libraryId: \"/mongodb/mongodbatlas\" and query: \"authentication requirements\"","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"This provides version-aware documentation directly, as an alternative to WebSearch.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Automated Workflows","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"CI/CD Integration","type":"text"}]},{"type":"paragraph","content":[{"text":"Use the deterministic skill-level CI gate as the blocking check:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"bash scripts/run_ci_checks.sh --require-shellcheck","type":"text"}]},{"type":"paragraph","content":[{"text":"This gate runs:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Shell syntax checks (","type":"text"},{"text":"bash -n","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Python syntax checks (","type":"text"},{"text":"python3 -m py_compile","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Python regression tests (","type":"text"},{"text":"test/test_detect_custom_resources.py","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Shell regression tests (","type":"text"},{"text":"test/test_validate_terragrunt.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"ShellCheck linting (required in CI when ","type":"text"},{"text":"--require-shellcheck","type":"text","marks":[{"type":"code_inline"}]},{"text":" is set)","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"After that gate passes, run environment-dependent validation in jobs that have Terragrunt/Terraform credentials configured:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"#!/bin/bash\n# ci-validate.sh\n\nset -euo pipefail\n\necho \"Running deterministic validator checks...\"\nbash scripts/run_ci_checks.sh --require-shellcheck\n\necho \"Installing dependencies...\"\n# Install terragrunt, terraform, tflint, trivy/checkov\n\necho \"Detecting custom resources...\"\npython3 scripts/detect_custom_resources.py . --format json > custom_resources.json\n\n# Could integrate with automated documentation lookup here\n\necho \"Running validation suite...\"\nSKIP_PLAN=true SKIP_BACKEND_INIT=true bash scripts/validate_terragrunt.sh .\n\necho \"Validation complete!\"","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Pre-commit Hook","type":"text"}]},{"type":"paragraph","content":[{"text":"Example pre-commit hook for local development:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"#!/bin/bash\n# .git/hooks/pre-commit\n\n# Format check\nterragrunt hcl fmt --check || {\n echo \"HCL formatting issues found. Run: terragrunt hcl fmt\"\n exit 1\n}\n\n# Quick HCL syntax validation (Terragrunt 0.93+)\n# Note: For full validation, use: terragrunt init && terragrunt validate\n# But that requires credentials. HCL format check catches syntax errors.\n\necho \"Pre-commit validation passed!\"","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Troubleshooting Guide","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Validation Modes and Exit Semantics","type":"text"}]},{"type":"paragraph","content":[{"text":"validate_terragrunt.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":" derives mode from the target directory and changes the Terragrunt command path accordingly:","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Mode","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Directory shape","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Terragrunt HCL check","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Terraform check","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Exit semantics","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"single","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"terragrunt.hcl","type":"text","marks":[{"type":"code_inline"}]},{"text":" (or ","type":"text"},{"text":"terragrunt.stack.hcl","type":"text","marks":[{"type":"code_inline"}]},{"text":") in target dir","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"terragrunt hcl validate","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"terragrunt validate","type":"text","marks":[{"type":"code_inline"}]},{"text":" (with ","type":"text"},{"text":"terragrunt init","type":"text","marks":[{"type":"code_inline"}]},{"text":" unless skipped)","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Any syntax/validate failure exits non-zero","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"multi","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Nested units exist below target","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"terragrunt hcl validate --all","type":"text","marks":[{"type":"code_inline"}]},{"text":" (fallback to plain ","type":"text"},{"text":"hcl validate","type":"text","marks":[{"type":"code_inline"}]},{"text":" if ","type":"text"},{"text":"--all","type":"text","marks":[{"type":"code_inline"}]},{"text":" is unsupported)","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"terragrunt run --all validate","type":"text","marks":[{"type":"code_inline"}]},{"text":" (with ","type":"text"},{"text":"run --all init","type":"text","marks":[{"type":"code_inline"}]},{"text":" unless skipped)","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Any unit failure exits non-zero","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"root-only","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"root.hcl","type":"text","marks":[{"type":"code_inline"}]},{"text":" only, no unit in target dir","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Warn and skip","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Warn and skip","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Returns success (0) for these skipped steps","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"none","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"No recognized Terragrunt config files","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Error","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Error","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Returns non-zero","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Debug Mode","type":"text"}]},{"type":"paragraph","content":[{"text":"Enable debug output for troubleshooting:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Terragrunt debug\nTERRAGRUNT_DEBUG=1 terragrunt plan\n\n# Terraform trace\nTF_LOG=TRACE terragrunt plan","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Common Error Patterns","type":"text"}]},{"type":"paragraph","content":[{"text":"\"Error: Module not found\"","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Clear cache: ","type":"text"},{"text":"rm -rf .terragrunt-cache","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Re-initialize: ","type":"text"},{"text":"terragrunt init","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"paragraph","content":[{"text":"\"Error: Provider not found\"","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Check provider configuration","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Run custom resource detection","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Use WebSearch to find correct provider source and version","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Verify required_providers block","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"\"Error: Invalid function call\"","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Check Terragrunt version compatibility","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Review function syntax in documentation","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"\"Cycle detected in dependency graph\"","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Review dependency chains","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Consider refactoring into single module","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Use data sources instead of dependencies","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"\"Error acquiring state lock\"","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Check if another process is running","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Verify DynamoDB table (for S3 backend)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Force unlock if safe: ","type":"text"},{"text":"terragrunt force-unlock \u003cLOCK_ID>","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"paragraph","content":[{"text":"\"Error: unknown command\" (Terragrunt 0.93+)","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Terragrunt 0.93+ has a new CLI with breaking changes","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Commands like ","type":"text"},{"text":"render-json","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"validate-inputs","type":"text","marks":[{"type":"code_inline"}]},{"text":" are deprecated","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Use ","type":"text"},{"text":"terragrunt run -- \u003ccommand>","type":"text","marks":[{"type":"code_inline"}]},{"text":" for custom/unsupported commands","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Replace ","type":"text"},{"text":"graph-dependencies","type":"text","marks":[{"type":"code_inline"}]},{"text":" with ","type":"text"},{"text":"dag graph","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"See: https://terragrunt.gruntwork.io/docs/migrate/cli-redesign/","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Output Interpretation","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Success Indicators","type":"text"}]},{"type":"paragraph","content":[{"text":"✅ ","type":"text"},{"text":"All checks passing:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"All HCL files properly formatted","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Inputs are valid","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Terraform configuration is valid","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"No linting issues","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"No critical security issues","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Valid dependency graph","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Plan generated successfully","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Warning Indicators","type":"text"}]},{"type":"paragraph","content":[{"text":"⚠️ ","type":"text"},{"text":"Review needed:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Security warnings from Trivy/Checkov/tfsec (non-critical)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Linting suggestions (best practices)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Deprecated provider features","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Missing recommended configurations","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Error Indicators","type":"text"}]},{"type":"paragraph","content":[{"text":"✗ ","type":"text"},{"text":"Must fix:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Format errors","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Invalid inputs","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Terraform validation failures","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Circular dependencies","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Provider authentication failures","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"State locking errors","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Advanced Usage","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Custom Validation Rules","type":"text"}]},{"type":"paragraph","content":[{"text":"Create custom tflint rules by adding ","type":"text"},{"text":".tflint.hcl","type":"text","marks":[{"type":"code_inline"}]},{"text":":","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"hcl"},"content":[{"text":"plugin \"terraform\" {\n enabled = true\n preset = \"recommended\"\n}\n\nplugin \"aws\" {\n enabled = true\n version = \"0.27.0\"\n source = \"github.com/terraform-linters/tflint-ruleset-aws\"\n}\n\nrule \"terraform_naming_convention\" {\n enabled = true\n}","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Custom Security Policies","type":"text"}]},{"type":"paragraph","content":[{"text":"Create custom tfsec policies by adding ","type":"text"},{"text":".tfsec/config.yml","type":"text","marks":[{"type":"code_inline"}]},{"text":":","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"yaml"},"content":[{"text":"minimum_severity: MEDIUM\nexclude:\n - AWS001 # Example: exclude specific rules","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Dependency Graph Analysis","type":"text"}]},{"type":"paragraph","content":[{"text":"Analyze complex dependency chains:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Generate detailed graph (Terragrunt 0.93+ syntax)\nterragrunt dag graph > graph.dot\n\n# Convert to visual format\ndot -Tpng graph.dot > graph.png\ndot -Tsvg graph.dot > graph.svg\n\n# Analyze for circular dependencies\ngrep -A5 \"cycle\" \u003c(terragrunt dag graph 2>&1)","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Resources","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Scripts","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"scripts/validate_terragrunt.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Comprehensive validation suite","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"scripts/detect_custom_resources.py","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Custom provider/module detector","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"References","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/best_practices.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Comprehensive best practices guide covering:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Directory structure patterns","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"DRY principles and configuration sharing","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Dependency management","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Security best practices","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Testing and validation workflows","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Common anti-patterns to avoid","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Troubleshooting guides","type":"text"}]}]}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"External Documentation","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Terragrunt Documentation","type":"text","marks":[{"type":"link","attrs":{"href":"https://terragrunt.gruntwork.io/docs/","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Terraform Best Practices","type":"text","marks":[{"type":"link","attrs":{"href":"https://www.terraform-best-practices.com/","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Terraform Registry","type":"text","marks":[{"type":"link","attrs":{"href":"https://registry.terraform.io/","title":null}}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Done Criteria","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Docs and scripts agree on one canonical executable workflow.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Fixture runs are deterministic via:","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"python3 test/test_detect_custom_resources.py","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"bash test/test_validate_terragrunt.sh","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Validation and security failures are reported with correct non-zero exits.","type":"text"}]}]}]}]},"metadata":{"date":"2026-11-02","name":"terragrunt-validator","author":"@skillopedia","source":{"stars":331,"repo_name":"cc-devops-skills","origin_url":"https://github.com/akin-ozer/cc-devops-skills/blob/HEAD/devops-skills-plugin/skills/terragrunt-validator/SKILL.md","repo_owner":"akin-ozer","body_sha256":"ca994497d009bcf013b542231ffe89be84c21bebb80c4e449d5795fc8e8f55f7","cluster_key":"6866235a3a23c746ce69c782dc6e83be1a3260d2280b8859a1cbe73cc49dcf56","clean_bundle":{"format":"clean-skill-bundle-v1","source":"akin-ozer/cc-devops-skills/devops-skills-plugin/skills/terragrunt-validator/SKILL.md","text_bytes":95369,"source_commit":"276af751e659315aaf56d3ad13d7c26f4e72e28a","skills_sh_rank":16420,"attachment_count":20,"text_attachments":20,"attachment_storage":"skill_attachments","binary_attachments":0},"cluster_size":1,"skill_md_path":"devops-skills-plugin/skills/terragrunt-validator/SKILL.md","import_metadata":{"date":"2026-11-02","author":"@skillopedia","version":"v1","category":"devops-infrastructure","category_label":"DevOps"},"exact_dupes_collapsed_into_this":0},"version":"v1","category":"devops-infrastructure","import_tag":"skills-sh-top20k-2026-11","description":"Validate, lint, audit, or check Terragrunt .hcl/terragrunt.hcl files, stacks, modules, compliance."}},"renderedAt":1793661689560}

Terragrunt Validator Overview This skill provides comprehensive validation, linting, and testing capabilities for Terragrunt configurations. Terragrunt is a thin wrapper for Terraform/OpenTofu that provides extra tools for keeping configurations DRY (Don't Repeat Yourself), working with multiple modules, and managing remote state. Use this skill when: - Validating Terragrunt HCL files ( .hcl, terragrunt.hcl, terragrunt.stack.hcl) - Working with Terragrunt Stacks (unit/stack blocks, ) - Performing dry-run testing with - Linting Terragrunt/Terraform code for best practices - Detecting and resea…