Setting levels
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.
The levels
Section titled “The levels”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.
Keys by root glob
Section titled “Keys by root glob”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.
Environments in one repo
Section titled “Environments in one repo”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 } } }A Terragrunt repo takes its roles from terragrunt.credentials, and its backends and versions from the units’ own remote_state and pins. The environments set each one’s gate:
terragrunt:
credentials:
"live/prod/**": { plan: arn:aws:iam::222222222222:role/prod-plan, apply: arn:aws:iam::222222222222:role/prod-apply }
"live/dev/**": { plan: arn:aws:iam::111111111111:role/dev-plan, apply: arn:aws:iam::111111111111:role/dev-apply }
environments:
prod: { roots: ["live/prod/**"], gate: always }
dev: { roots: ["live/dev/**"] }An Atmos instance’s root is <stack>/<component>, so globs over stacks give each one its roles, and the stacks’ own imports keep setting what the component sees:
oidc:
plan_role: arn:aws:iam::111111111111:role/terragucci-plan
apply_role: arn:aws:iam::111111111111:role/terragucci-apply
roles:
"prod/*": { plan: arn:aws:iam::222222222222:role/prod-plan, apply: arn:aws:iam::222222222222:role/prod-apply }
environments:
prod: { roots: ["prod/*"], gate: always }Read at base
Section titled “Read at base”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).
- Control repo for
defaults, projects andreconcile. - terragucci.yml keys for each key.
- Separate environments for the forge and cloud settings beside
environments.
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.