Drift checks
Scheduled, read-only drift checks that alert Slack, Teams, Discord, Google Chat or any webhook, and tag who changed each resource.
Drift checks run read-only plans on a schedule and tell your team when what's deployed no longer matches the code.
Set up a schedule
channels: # in repos.yaml; secrets stay in env vars
infra-alerts: { type: slack, channel: "#infra-drift", token_env: TERIAC_SLACK_TOKEN }
ops-teams: { type: teams, webhook_url_env: TERIAC_TEAMS_WEBHOOK }
people: # optional: git/GitHub → chat identity
- { name: Bob Martins, github: bob-m, slack: U04BOBM, teams: bob@acme.onmicrosoft.com }
repos:
- id: github.com/acme/infra
drift: # or under `drift:` in the repo's teriac.yaml
- name: prod-nightly
projects: ["envs/prod", "live/prod/**"]
branch: main
schedule: "0 6 * * 1-5"
timezone: Europe/Berlin
notify: [infra-alerts, ops-teams]Drift schedules in repos.yaml need an exact repo id, not a glob. A repo can also keep its own
schedules under drift: in teriac.yaml (turn that off with allow_repo_schedules: false).
Schedule fields
| Field | Default | |
|---|---|---|
name |
Shown in alerts and the dashboard | |
projects |
required | Project names, folders or globs |
branch |
the repo's default branch | Branch to check |
schedule |
required | Cron, e.g. 0 6 * * 1-5 (06:00 on weekdays) |
timezone |
UTC |
An IANA name, e.g. Europe/Berlin |
notify |
required | Channel names from channels |
notify_when |
changed |
changed: only when findings change. always: every run |
include_pending |
true |
Also report changes that were merged but never applied |
fix_pr |
false |
Open a PR that updates the code to match what's running. See fix PRs |
Channels
Alerts go to Slack, Teams, Discord, Google Chat or any webhook. Keep tokens and webhook URLs in env
vars and name them with token_env or webhook_url_env.
type |
Settings | Mentions |
|---|---|---|
slack |
token_env + channel (recommended), or webhook_url_env |
@ the person, by people or email lookup (bot token) |
teams |
webhook_url_env |
By people.teams |
discord |
webhook_url_env |
By people.discord |
googlechat |
webhook_url_env |
Names in bold |
webhook |
webhook_url_env |
JSON payload, see below |
channels:
infra-alerts: { type: slack, channel: "#infra-drift", token_env: TERIAC_SLACK_TOKEN }
ops-teams: { type: teams, webhook_url_env: TERIAC_TEAMS_WEBHOOK }
dev-discord: { type: discord, webhook_url_env: TERIAC_DISCORD_WEBHOOK }
gchat: { type: googlechat, webhook_url_env: TERIAC_GCHAT_WEBHOOK }
pagerduty: { type: webhook, webhook_url_env: TERIAC_DRIFT_WEBHOOK }A Slack bot token needs chat:write, plus users:read.email to find people by email.
Webhook payload
The webhook type posts JSON you can route anywhere:
{
"event": "teriac.drift",
"report": { "projects": [ … ] },
"people": [
{ "project": "live/prod/vpc", "people": [ { "name": "Bob Martins", "person": { … } } ] }
]
}People
people maps code and platform identities to chat users, so alerts mention the right person:
people:
- name: Bob Martins
github: bob-m
emails: [bob@acme.com, bob.martins@users.noreply.github.com]
slack: U04BOBM # Slack member ID
teams: bob@acme.onmicrosoft.com # Teams UPN
discord: "123456789012345678" # Discord user IDWithout an entry, Slack (with a bot token) is asked to find the person by their commit email.
With Helm
Put the same config under repos, and load the tokens with extraEnvFrom:
repos:
channels:
infra-alerts: { type: slack, channel: "#infra-drift", token_env: TERIAC_SLACK_TOKEN }
repos:
- id: github.com/acme/infra
drift:
- { name: prod-nightly, projects: ["prod/**"], schedule: "0 6 * * 1-5", timezone: Europe/Berlin, notify: [infra-alerts] }
extraEnvFrom:
- secretRef: { name: teriac-chat-tokens } # holds TERIAC_SLACK_TOKENWhat a check reports
Each run checks the branch with read-only plans (-refresh-only -lock=false) and reports:
- changes made outside Terraform, such as a security group edited in the console;
- changes that were merged but never applied.
Each drifted resource tags the last person who changed its code, found from the git history of its
block, plus the last person who applied that project. Matching to chat users uses people, or Slack's
lookup by email.
When it notifies
A message goes out only when the findings change. Set notify_when: always to send one every run.
Failed checks always notify.
Fix PRs
New in 0.2.0.
With fix_pr: true on a schedule, teriac opens a PR that updates the code to match what is running,
and links it in the drift message.
drift:
- { name: prod-nightly, projects: ["prod/**"], schedule: "0 6 * * 1-5", notify: [infra-alerts], fix_pr: true }- It only edits values written plainly in the resource block (a string, number or
true/false) that still match the last applied value. - Values from variables, expressions, modules and nested blocks are listed in the PR for a person to handle, with who last changed them.
- Accept the change: merge the PR. Undo it: close the PR and apply the base branch, which puts things back.
- One PR per project at a time, on the branch
teriac/drift-fix/<project>.
The GitHub App needs Contents: read & write for this (read is enough otherwise).
Running and pausing
- Turn all schedules off with
TERIAC_DRIFT=false. - Admins can start a check from the dashboard, or with
POST /api/drift/run?key=…(keys are listed byGET /api/drift/schedules). - Findings and history are in the dashboard's Drift page and
GET /api/drift.