Skip to content

Turn on drift checks

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/turn-on-drift-checks/.
Add a `drift:` cron to terragucci.yml, run `npx terragucci config check --json`
and `npx terragucci init`, and open a pull request. Tell me the GitLab schedule
or token scope I must set.
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`.

A scheduled tf-drift run that plans every root, and one issue that groups what drifted like a pull-request plan.

You need Why
The pipeline from Get your first plan note init adds the drift job to it
A plan role that can read your cloud drift plans use the same read-only role as pull requests
Terraform or OpenTofu roots, or Terragrunt units the issue names roots, or units in a Terragrunt repo
A cron expression in UTC forge schedulers read cron in UTC
  1. Set a schedule in terragucci.yml. Pick an odd minute; forge schedulers are busiest on the hour.

    drift: "17 4 * * *"
  2. Check the file.

    Terminal window
    npx terragucci config check
    terragucci.yml: ok
    approval: ledger (the default)

    A bad value is listed as a problem, and the command exits 2.

  3. Write the pipeline again and merge it.

    Terminal window
    npx terragucci init

    init adds a drift job with the cron. A workflow_dispatch with no pr input runs it by hand.

  4. Wait for the first run, or start one.

    terragucci stage tf-drift plans each root with -refresh-only, comparing state with real objects. Only an object changed or deleted outside Terraform counts as drift, and a merged but unapplied change does not. A choudoufu root under live resource markers plans in full instead (below).

  5. Read the issue.

    Each project has at most one open issue, terragucci: drift found, which every run updates. A root that cannot be refreshed fails the job; drift alone does not.

    Event The issue
    Drift found opened, or updated if one is open
    Every root clean closed
    A root could not be planned left as it is
    The drift issue on GitHub, opened by github-actions: 1 of 15 roots has drifted, envs/staging/orders, whose local_file.jobs_queue no longer exists, with the full report in the artifacts of the runThe drift issue on GitHub, opened by github-actions: 1 of 15 roots has drifted, envs/staging/orders, whose local_file.jobs_queue no longer exists, with the full report in the artifacts of the run
  6. Remediate the drift.

    With respond.drift left at its default, the drift job also opens a pull request from terragucci/drift. It writes the live value where the root’s own resource block sets a literal, and adds an import block for a resource the state does not hold. Responses lists what is only reported.

    To Do Then
    keep the live value read the pull request, comment /terragucci plan on it for its plan note (Re-plan from a comment), and merge it the apply job applies it in waves, behind the gate like any change
    put the object back close the pull request and apply the root again the apply makes the object match the code

    A coding agent can take what the pull request cannot write (a value set through a variable or a module). Set agent.drift and the drift issue starts an agent that returns its change as a pull request.

    Nothing in the drift job applies or merges. Each wave’s approval and each run’s report stay in the audit trail.

    Under respond.drift: attribute the issue also names each attribute’s changer from the audit log (Drift attribution):

    A drift issue in Forgejo for the root app: the queue's visibility_timeout_seconds changed, and Who changed it puts that down to a person, SetQueueAttributes by alice, from the audit logA drift issue in Forgejo for the root app: the queue's visibility_timeout_seconds changed, and Who changed it puts that down to a person, SetQueueAttributes by alice, from the audit log
  7. Watch for an overdue note.

    When the schedule stops, the next pull request’s plan note says why the drift checks are overdue (Overdue drift checks).

With apply.branches, the drift job checks a root that another branch applies against that branch’s code. The job runs on the default branch and plans the root in a checkout of its branch fetched from origin. The log names the branch and its commit:

apply.branches: envs/prod/app plans from release at 3f9a1c2e, the branch that applies it

A provider, backend or version the branch sets differently is the one the check uses, so a difference between the two branches is never reported as drift. A root whose branch cannot be fetched fails its check with the reason; it is never checked against the default branch instead. In a Terragrunt repo, a branch’s units plan with one run --all in that branch’s checkout.

A choudoufu root under live resource markers keeps no state to refresh: every plan of it reads the live system, and choudoufu refuses -refresh-only there. tf-drift plans such a root in full, and each change the plan would make is drift, read from the live side:

The plan would The issue says
change an attribute the attribute changed
create a resource it no longer exists
destroy a resource it exists outside the code

A change merged to the default branch but not applied yet also shows here, since the plan cannot tell it from a change made outside choudoufu. The rest of the drift job is the same: one issue, respond.drift and attribution.

  • Stages lists what tf-drift reads and writes.

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.