Skip to content

Setting levels

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/concepts/setting-levels/.
List each root in this repo and the settings it ends up with: its binary version, its plan and apply identities, its environment and gate, the generated files' backend and providers, and its secrets, by applying each glob-keyed key's matching rule from the page to terragucci.yml (and, in a control repo, `defaults` and the project's block).
Read only. 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`.

One repo works with no levels at all: its terragucci.yml sets what the defaults got wrong. A control repo keeps many repos alike, globs give each environment its own identities, versions and generated files without a second tool, and a repo on Terragrunt, Atmos or Terramate keeps the inheritance it has.

Each level takes the one above it as its default and sets only what differs.

Level Sets Edited by Wins over
terragucci’s built-in defaults gate: on-destructive, no drift, the forge’s runtime, tips on (Defaults with no file) nobody nothing
a control repo’s defaults what every project shares; any key but url and rollouts the control repo’s reviewers the built-in defaults
a project’s block in the control repo what differs for one project the control repo’s reviewers defaults
the project’s terragucci.yml in a single repo, its settings; in a control repo, reconcile writes the keys the jobs read into it, and an own file whose value for such a key differs fails that project’s reconcile, naming the key the project’s team the levels above it, in a single repo
globs per root a version, identities, an environment, generated files, secrets, a policy override and approval rules for the roots each glob matches (below) whoever edits the level the key is set at the key’s value for the rest of the roots
the tool’s own inheritance Terragrunt includes, Atmos imports and Terramate globals, which decide what the binary sees for each unit, instance or stack the repo’s team, as before terragucci reads it and does not change it

Between the first three levels, a key is replaced whole by the later level, except these maps:

Key Merges
env key by key
oidc, policy, waves, apply, respond, terragrunt one level deep: each key under them is replaced whole, so a project’s oidc.roles replaces the defaults’ map and the two are never combined
environments by environment name, then each environment’s keys replaced one by one; an environment’s roots and oidc are replaced whole
generate as an overlay: a backend of the same type merges its arguments and of another type replaces it, providers merge by provider, dirs and roots merge by key, and null removes a value

Every other key, a list or a map, is replaced whole: roots, approval_rules, secrets, ephemeral, a version map, sources, cost, notify, pass, reports, runner and the rest.

A glob’s * matches within one path segment, ** across segments (and a/** matches a too), and ? one character. Matching is anchored at both ends, and there are no character classes or brace sets.

Key When several globs match a root A root no glob matches
version (map) the first in the file’s order the root’s version file, then an exact required_version, then the job’s version
oidc.roles, oidc.gcp.roles, oidc.azure.roles the first in the file’s order the job’s plan and apply identities
terragrunt.credentials the first in the file’s order; a unit that sets its own iam_role keeps it the unit’s own configuration and the job’s credentials
environments.<name>.roots refused: config check names a root two environments match, and a root given identities by both its environment and oidc.roles the top-level gate, oidc, runner and parallelism
generate.dirs every match, in the file’s order, each over the last; then generate.roots for the root’s exact path the repo-level generate keys
secrets[].roots every matching entry; config check refuses two entries that give one root the same variable no values from a store
ephemeral.roots any match gets a copy no copy
apply.branches refused: config check names a root two branches’ globs match applies from the default branch
approval_rules[].roots every rule a root of the wave matches, and each must be met what the approval mode needs
policy.override entries every matching entry adds the people it names, when its rules cover the rules that denied the root the names listed without an entry
waves.canary any match puts the root in wave 1 its place in the dependency order

“The file’s order” is the order of the keys as written. After a control repo merge it is still one level’s order, since a map such as oidc.roles comes whole from one level.

Each environment below gets its own binary version, identities and generated backend:

version:
"envs/prod/**": "1.10.6"
"envs/dev/**": "1.11.2"
environments:
prod:
roots: ["envs/prod/**"]
gate: always
oidc: { plan_role: arn:aws:iam::222222222222:role/prod-plan, apply_role: arn:aws:iam::222222222222:role/prod-apply }
dev:
roots: ["envs/dev/**"]
oidc: { plan_role: arn:aws:iam::111111111111:role/dev-plan, apply_role: arn:aws:iam::111111111111:role/dev-apply }
generate:
backend:
s3: { key: "{root}/terraform.tfstate", region: us-east-1, use_lockfile: true }
dirs:
"envs/prod/**": { backend: { s3: { bucket: acme-prod-state } } }
"envs/dev/**": { backend: { s3: { bucket: acme-dev-state } } }

These keys are read from the config at base, the applied commit’s first parent for an apply and the target branch for a pull request’s plan, so a pull request that edits one is decided by the default branch’s value and its own edit takes effect once it merges:

Key Decides
approval which approvals count (Approval modes)
approval_rules how many approvers, and which roles
apply.point the decision point a wave gate asks
policy, its directory or source, policy.override and policy.outcomes what the policy denies, holds or lets through, and who may override; a base with no policy keeps the checkout’s, so a pull request can add a policy and cannot loosen one
sources the allowed module and provider sources, the same way as policy
gate each wave’s gate, with the pipeline’s --gate: a wave takes the stricter of the two
cost.approve_above the amount over which a wave waits
environments each wave’s gate
ephemeral the roots a pull request copies, and the copies’ lifetime
modules, once the base has require: attested which releases a root may pin
agent.login, agent.token_env the accounts whose approvals never count

The signers file, the roles in .chant/trust.json and chant.workspace.json are read at base too.

A pull request applied before merge reads every other key at base as well (Apply before merge).

terragucciHelp setting it up: Intentius

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.