DEV Community

Cover image for Start Safe: Terragrunt Import for Multi-Account AWS
TerraformMonkey
TerraformMonkey

Posted on • Originally published at controlmonkey.io

Start Safe: Terragrunt Import for Multi-Account AWS

Terragrunt Import lets you bring brownfield infrastructure under Terraform control across multi-repo and multi-account setups. Done right, you’ll avoid state drift, unstable addresses, and risky access patterns.

The goal is a reproducible, auditable workflow with clean plans and minimal permissions. Use a consistent remote state, pin tooling versions, and validate every step in CI.


🔎 At a Glance: Terragrunt Import Best Practices

  • ✅ Standardize remote state and lock it
  • 📌 Pin Terraform, providers, and Terragrunt versions
  • 🧾 Document intent with Terraform import blocks
  • 🤖 Automate plans and halt on drift or diffs
  • 🔐 Use least-privilege, short-lived credentials

Mini-story: An engineer imported dozens of resources on a laptop with a newer provider than CI. The next pipeline showed a wall of “changes” — all caused by version drift. Pinning would have caught this earlier.

If you’re managing multiple accounts and environments, keeping configuration clean and consistent can be a real challenge.

👉 Check out Terragrunt Less Verbose for tips to reduce boilerplate and simplify Terragrunt structure across large repos.


🧱 Do: Prepare State, Providers & Repo for Safe Terragrunt Import

  • Use a remote backend with locking + encryption (S3 + DynamoDB lock, GCS, or Azure Blob). Inherit backend config via a root terragrunt.hcl to avoid divergent state and concurrent writes.
  • Pin versions for Terraform, providers, and Terragrunt. Run terraform init -upgrade only in controlled windows.
  • Validate in CI with terraform validate and terraform plan -detailed-exitcode gates.
  • Preflight with snapshots: enable bucket/container versioning and take a state backup before each import; start with a read-only discovery run.
# example CI guard
terraform fmt -check
terraform validate
terraform plan -detailed-exitcode   # exit 2 on diff; fail the job
Enter fullscreen mode Exit fullscreen mode

🧭 Do: Use Import Blocks + Terragrunt Hooks for Clear, Stable Addresses

Prefer Terraform ≥ 1.5 import blocks to document import intent in code and keep resource addresses stable across runs. Combine with Terragrunt hooks to (a) generate import IDs, (b) run a plan immediately after import, and (c) fail on any unexpected diff.

Start with a skeleton HCL: declare essential arguments only; add temporary lifecycle.ignore_changes for noisy attributes until parity is verified.

Caveat: import blocks require Terraform 1.5+.

Canonical snippet (HCL):

# modules/storage/main.tf
resource "aws_s3_bucket" "logs" {
  bucket = var.bucket_name

  lifecycle {
    ignore_changes = [tags] # temporary while achieving parity
  }
}

# modules/storage/import.tf (Terraform ≥ 1.5)
import {
  to = aws_s3_bucket.logs
  id = "my-company-logs"
}
Enter fullscreen mode Exit fullscreen mode

Live config with Terragrunt:

# live/prod/storage/terragrunt.hcl
terraform {
  source = "../../../modules/storage"
}

inputs = {
  bucket_name = "my-company-logs"
}

# Optional Terragrunt hook: force a plan after import and fail on drift
after_hook "after_import_plan" {
  commands = ["import"]
  execute  = ["bash", "-lc", "terraform plan -detailed-exitcode || exit 1"]
}
Enter fullscreen mode Exit fullscreen mode

Keep module paths stable across environments so resource addresses never change.


🚫 Don’t: Refactor Modules Mid-Import or Apply Without a Clean Plan

  • Don’t refactor module names, move modules, or rename resources during an import — it changes addresses and breaks state mapping.
  • Never apply after an import unless the plan is clean (no unintended creates/destroys). Enforce with -detailed-exitcode in CI.
  • If you discover an address mismatch, fix it with:
terraform state mv 'aws_s3_bucket.logs' 'aws_s3_bucket.logs_new'
Enter fullscreen mode Exit fullscreen mode

Caveat: state mv ops should be reviewed in PRs and run from the same pinned toolchain as your plans.


🔐 Do: Enforce Least-Privilege & Short-Lived Access

  • Use assume-role with external IDs/MFA and short sessions, scoped to import-only APIs for the target services.
  • Separate read-only discovery from write operations; rotate credentials; store secrets securely in CI.
  • Keep audit trails: confirm who imported what and when using provider/cloud logs (e.g., CloudTrail). Keep local CI run metadata as a cross-check (provider logs can lag).
# Example: short-lived AWS session (assume-role)
aws sts assume-role \
  --role-arn arn:aws:iam::123456789012:role/terraform-importer \
  --role-session-name terragrunt-import
Enter fullscreen mode Exit fullscreen mode

🗂️ Example: Importing a Storage Bucket (Pattern Applies Broadly)

  1. Create a minimal resource block and keep the terragrunt.hcl path stable.
  2. Add a Terraform import block with the bucket’s canonical ID.
  3. terraform init, then nudge Terragrunt to plan:
terragrunt run-all plan -detailed-exitcode
Enter fullscreen mode Exit fullscreen mode
  1. The plan should show no changes except legitimate drift.
  2. If noise appears (e.g., tags or server-generated fields), add temporary ignore_changes, reconcile code to reality, then remove ignores once parity is achieved.
  3. Commit the import block + configuration together so future plans remain clean.

If you hit unexpected diffs or failed imports, read The Complete Terraform Errors Guide to decode plan output, debug root causes, and avoid destructive applies.


🧰 Bring It Together with Guardrails

A disciplined Terragrunt Import flow yields reproducible, auditable results with clean plans and least-privilege access. Codify intent with import blocks, keep addresses stable, and block applies on drift.

Looking for acceleration? ControlMonkey can help with discovery, safe sequencing, and policy guardrails across multi-account environments.

👉 Request a demo to see it in action.


💬 Discussion

What’s your Terragrunt import playbook for multi-account AWS?

Which drift signals or CI gates have saved you from bad applies? Share your setup in the comments!

Top comments (0)