Enterprise IaC Architecture & Terragrunt Governance¶
Skill metadata
Name enterprise-iac-governance-terragrunt · Level staff · Tags terragrunt terraform opa governance devops-core
"Enterprise IaC governance: Terragrunt DRY multi-account layouts, generated backends and providers, dependency graphs, and policy-as-code gates with OPA and Conftest. Use when Terraform has been copy-pasted across many accounts or environments, or when non-compliant resources such as unencrypted or untagged S3 buckets must be blocked in CI before apply rather than found afterwards."
Source: skills/devops-core/senior-staff-architect/enterprise-iac-governance-terragrunt/SKILL.md
When to Use This Skill¶
Triggers — load this skill when:
- Terraform has been copy-pasted across accounts/environments and needs DRY structure
- Infrastructure policy must be enforced in CI before apply (OPA/Conftest/Sentinel)
- State, backend, and provider configuration must be generated rather than duplicated
Route elsewhere when:
- Single-repo module design fundamentals ->
terraform-iac-modules - Runtime posture scanning of deployed resources ->
cloud-security-posture-cspm-cis
1. Multi-Account Terragrunt Architecture¶
live/
├── terragrunt.hcl # Root configuration (remote state, global tags)
├── _envcommon/
│ └── vpc.hcl # Reusable module definitions
├── prod/
│ ├── account.hcl # Account-specific variables & role ARNs
│ └── us-east-1/
│ └── networking/
│ └── terragrunt.hcl # Includes _envcommon/vpc.hcl
Root Terragrunt Remote State & Provider Injection¶
remote_state {
backend = "s3"
generate = {
path = "backend.tf"
if_exists = "overwrite_terragrunt"
}
config = {
bucket = "tf-state-${get_aws_account_id()}"
key = "${path_relative_to_include()}/terraform.tfstate"
region = "us-east-1"
encrypt = true
dynamodb_table = "tf-locks"
}
}
2. Policy-as-Code Enforcements (OPA Rego)¶
Enforce mandatory tagging and encryption across all Terraform plans before applying:
package terraform.analysis
default allow = false
# Rule: S3 Buckets must have server-side encryption enabled
deny[msg] {
resource := input.resource_changes[_]
resource.type == "aws_s3_bucket"
not resource.change.after.server_side_encryption_configuration
msg := sprintf("S3 Bucket '%v' must specify server_side_encryption_configuration", [resource.address])
}
allow {
count(deny) == 0
}
3. Governance Gate in CI (Conftest)¶
- name: Terragrunt plan to JSON
run: |
terragrunt run-all plan -out=tfplan.binary --terragrunt-non-interactive
terragrunt run-all show -json tfplan.binary > tfplan.json
- name: Enforce policy
run: conftest test --policy policy/ --all-namespaces tfplan.json
Gate on the plan JSON, never on HCL source: only the plan reveals the resolved, post-variable, post-module values an apply will actually create.
4. Best Practices & Anti-Patterns¶
| Do | Don't |
|---|---|
Pin terraform_version and module source refs to immutable tags/SHAs |
Track main or float provider versions across accounts |
| Keep one state file per account/region/component (blast-radius sizing) | Keep a monolithic state for the whole organization |
Generate backend.tf / provider.tf via Terragrunt generate blocks |
Copy backend and provider stanzas into every leaf module |
Express cross-stack wiring with dependency blocks and outputs |
Hardcode ARNs, VPC IDs, or account numbers between stacks |
Fail the pipeline on policy deny before apply |
Detect violations only after resources exist |
| Assume a per-account role via OIDC at plan/apply time | Share one long-lived admin credential across environments |
Anti-patterns that reliably cause outages¶
run-all applyacross production without a reviewed, artifact-pinned plan.- Policy exceptions granted in code comments instead of an expiring, owned waiver file.
- Terragrunt used purely as a
terraformwrapper while duplication stays in leaf modules.