Skip to content

The same shop on Terragrunt

llms.txtlists every page for an agent
Optional: hand this page to your coding agentThe steps work by hand too.
Show the whole prompt
Read https://intentius.io/terragucci/tutorial/terragrunt/.
In the terragucci clone, run `just example-terragrunt up`, then `just example-terragrunt change module-bump` and `just example-terragrunt change new-service`. Summarize which units each plan note covers and why, and where it differs from the page.
Never apply, approve (a pull request review or `terragucci approve`), override a policy denial (`terragucci override`), use `--mode apply`, or merge; never touch `.chant/allowed_signers` or `chant/lifecycle`.

Each environment is a directory of units:

root.hcl state in the shop's bucket, and the AWS provider every unit gets
live/common.hcl settings every unit reads with read_terragrunt_config
live/dev/platform the environment's logs bucket
live/dev/orders a service; its dependency block reads platform's outputs
modules/service the module every service uses; it reads policy.json with file()

There is no roots: setting. On finding root.hcl init asks Terragrunt for the units and writes the pipeline, which the example already has. In your own repo run npx terragucci init at the root, and it prints:

found Terragrunt (root.hcl): 15 units in 5 waves from terragrunt find, terragrunt 1.1.6 (the image) calling tofu 1.13.1 (terragucci.yml), parallelism 16 (the s3 backend), forge forgejo (.forgejo/workflows)

Each wave is one dependency layer, the dev canary’s first. Its job plans with one terragrunt run --all, and a second run --all applies the saved plans once the gate lets it.

Wave Units Job
1 dev platform apply-wave-1
2 the four dev services, which read it apply-wave-2
3 staging and prod platform apply-wave-3
4 the staging and prod services, except prod search apply-wave-4
5 prod search, which goes out after prod orders apply-wave-5

Under the default on-destructive gate a wave that destroys or replaces something waits for an approval of its set digest. The example’s chant.workspace.json lists the five gates, so it runs approval: sealed and each approval must be sealed.

A service plans only after its platform applied, so it reads the platform’s real outputs.

The check job runs, in order:

  1. tofu fmt -check -recursive -diff ., for the modules the units call
  2. terragrunt hcl fmt --check
  3. terragrunt hcl validate --inputs
  4. terragucci check-pins, which checks each unit’s terraform { source } when modules.require: attested is set
  5. terragucci check-policy, which runs the policy’s tests when policy is set

An unformatted .hcl file fails the job by name:

Terminal window
just example-terragrunt change unformatted
The check job of the unformatted scenario's push run on the Terragrunt example in Forgejo: Format check and validate, every unit fails with the diff of live/dev/orders/owner.hcl and the error that the file needs formatting, exit code 1The check job of the unformatted scenario's push run on the Terragrunt example in Forgejo: Format check and validate, every unit fails with the diff of live/dev/orders/owner.hcl and the error that the file needs formatting, exit code 1

modules/service reads policy.json with file(), which Terragrunt’s change detection does not follow. terragucci adds the units it reaches, and says why:

Terminal window
just example-terragrunt change module-bump

The plan covers the 12 service units and none of the platforms. Each unit in the report names modules/service/policy.json as its reason.

The plan note on the module-bump pull request of the Terragrunt example in Forgejo: the 12 service units planned for the change to modules/service/policy.json, and no platform unitThe plan note on the module-bump pull request of the Terragrunt example in Forgejo: the 12 service units planned for the change to modules/service/policy.json, and no platform unit

A new unit and its upstream, in one change

Section titled “A new unit and its upstream, in one change”

The new-service scenario adds ledger, which makes a bucket, and billing, which reads the bucket’s name from it. Ledger has no outputs yet, so Terragrunt on its own would hand billing its mock_outputs.

Terminal window
just example-terragrunt change new-service

The plan job plans ledger, then plans billing on the bucket name from ledger’s plan, shop-tg-dev-ledger; the note lists billing as planned on ledger’s planned outputs. After the merge ledger applies in wave 1 and billing plans again in wave 2 on its real outputs.

Billing’s dependency block has no allow-list, so its mocks could stand in for apply. The report’s tips name it TF041, as for live/prod/email.

The example’s units are each in their own directory. A repo whose units come from a terragrunt.stack.hcl gets the same pipeline: every job runs terragrunt stack generate first, and the generated units take their waves from their dependency blocks. Explicit stacks lists what each stage does with them.

Map a plan role and an apply role by path; a unit that sets its own iam_role keeps it:

terragrunt:
credentials:
"live/prod/**": { plan: arn:aws:iam::111122223333:role/prod-plan, apply: arn:aws:iam::111122223333:role/prod-apply }

The generated pipeline has the details.

Tutorial step 10 of 11.

terragucci

These docs count page views and clicks with PostHog. They set no cookies, store nothing in your browser, and send nothing when your browser asks not to be tracked.