Skip to content

Choose choudoufu, Terraform, OpenTofu or 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/guides/use-a-binary/.
Run `npx terragucci init --dry-run --json` and tell me which binary it picked and why.
Set `binary:` in terragucci.yml only if that pick is wrong, run `npx terragucci init`, and open a pull request.
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`.

Every binary runs the same four stages: tf-check, tf-plan, tf-apply and tf-drift. choudoufu, the OpenTofu fork from the team behind terragucci, adds twelve features on top of OpenTofu’s.

choudoufu Terraform OpenTofu Terragrunt
State one record per resource in an S3 backend, and a tag on each resource; the state file is a cache your backend your backend each unit’s backend
What tf-check runs fmt -check, validate, live-check fmt -check, validate fmt -check, validate hcl fmt --check, hcl validate
What the report times each resource; slow provider calls, summed timings on a large estate and lock waits on a locking backend per-resource timings: not reported (Terraform sends no traces) each resource each unit, from Terragrunt’s run report
CI image terragucci-choudoufu, choudoufu 0.25.0 terragucci-terraform, Terraform 1.14.9 terragucci-tofu, OpenTofu 1.13.1 terragucci-terragrunt, Terragrunt 1.1.6 and OpenTofu 1.13.1
Version pin version only version, .terraform-version, or an exact required_version; per root too version, .opentofu-version, or an exact required_version; per root too terragrunt.version, or an exact terragrunt_version_constraint; per unit too, with the binary’s pins

A pin the image does not carry is installed in the job and checked against the release’s SHA256SUMS. Images are pinned by digest.

A root pinned by its own .opentofu-version, .terraform-version or exact required_version runs that version; the rest of its wave runs the job’s. version in terragucci.yml can also be a map of root glob to version:

binary: tofu
version:
"envs/legacy/*": "1.9.1"

The plan and apply jobs install each pinned version once and run each root with its own. The plan note and the report name each root’s binary and version:

Binaries: tofu 1.10.6 for `old` (.opentofu-version); tofu 1.13.1 for 1 root.

Run npx terragucci init after adding a pin, so the check job validates each root with its version too. A version per root gives the order pins are read in.

binary: choudoufu

On choudoufu, the stages add:

You get Where you see it
A live check in tf-check on every push, before the apply waves, with no cloud credentials the tf-check log and check report
One record per resource in an S3 backend, each write conditional, with no lock table or database to run. The state file is a cache: never the record of what you own, and losing it costs a refresh Record writes
Each address finds its real resource: the apply writes a tag on each resource and the next plan reads it back Live resource markers
How long a wave waited for a state lock, and how many tries it took, on a backend that locks the report, the trace and a metric
Which provider calls were slow, and the resource each was for the report’s provider calls
Reports that stay readable past a span budget on a large estate, with timings summed by resource type the report’s summed timings
Two applies of one estate that change different resources run at the same time; one that changes a resource another is applying waits for it, or, from a comment, is refused, before it reaches the cloud. A killed apply leaves nothing to release, and the next one refuses a plan it made stale Locking with choudoufu
A role scoped to one estate by its ownership tag: it applies that estate and is refused on another estate’s resources your cloud’s IAM
One record store bucket for every estate, each under its own prefix your bucket
A resource moved to another estate by rewriting its tags, or a state adopted into an estate, through a migration approved by digest Estates
A root that reads another estate’s outputs through data "terraform_estate_outputs" applies after the root whose live block owns that estate the layers init --dry-run counts, and each wave’s apply order
Drift checks on roots under live resource markers: each plan reads the live system, so tf-drift plans the root in full and reports what the plan would change back as drift the drift issue

init never picks choudoufu on its own: set binary: choudoufu or run npx terragucci init --binary choudoufu.

A live-check refusal fails tf-check and names its rule in the log. terragucci does not read a root’s required_version as a choudoufu release.

  1. See what init picked. The first line names the binary and why.

    Terminal window
    npx terragucci init --dry-run
    found 15 roots in 2 layers, tofu 1.13.1 (tofu on the path), forge github (the origin remote (github.com))

    binary: in terragucci.yml comes first, then --binary, then:

    Order Source Binary
    1 .opentofu-version tofu
    2 .terraform-version terraform
    3 .tofu files in a root tofu
    4 tofu on the path tofu
    5 terraform on the path terraform
    6 nothing found tofu

    If it picked the one you want, stop here.

  2. Put your tab’s binary: line in terragucci.yml, or run npx terragucci init --binary <name>.

  3. Write the pipeline again and commit it.

    Terminal window
    npx terragucci init
    found 15 roots in 2 layers, tofu 1.13.1 (terragucci.yml), forge github (the origin remote (github.com))
    updated .github/workflows/terragucci.yml
    using terragucci.yml
  • Use Terragrunt if your roots are Terragrunt units.
  • Stages lists the inputs, permissions and outputs of each stage.

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.