Drift & state issues

Use this page when the graph and your real infrastructure disagree, or when entities show a stale/missing drift status.

The graph is a projection, not the source of truth

Terrantula ingests your Terraform state (or Atmos stacks) into entities and stamps each one with where it came from and when. Your state backend remains the source of truth. "Drift" is the gap between what Terrantula last ingested and what the source says now — closing it means re-ingesting, never editing the graph directly.

Two kinds of drift

There are two separate notions of drift; matching your symptom to the right one saves time.

  • Sync drift (cross-source). Tracks an entity against the external source it was ingested from (a TF state file, an Atmos stacks dir, an S3 state). Surfaced as a per-entity status: current, stale, or missing, based on the entity's last_synced_at stamp and a 24-hour threshold. This is what you'll hit most.
  • Drift events (intra-Terrantula). A reconciler comparing the catalog's stored properties to what it observes at runtime inside Terrantula. Separate system, separate records. See the Drift events reference.

Entities showstale ormissing

SymptomGET /entities?driftStatus=stale returns rows, or the dashboard flags entities as stale/missing.

Cause

  • stale — the entity hasn't been re-synced within the 24h threshold. Its last_synced_at is old. Almost always: you imported once and haven't re-run the import since.
  • missing — the entity exists in Terrantula but was not present in the most recent source scan (e.g. the resource was removed from the state file).

Fix

  • For stale: re-run the import against the same source. Each successful import terraform / import-atmos re-stamps the touched entities with last_synced_at = now(), clearing the stale status.

    terrantula import terraform --state ./terraform.tfstate --config terrantula.yaml
  • For missing: decide whether the entity should still exist. If the resource is genuinely gone, drop the leftover entity with --replace (preview with --dry-run first):

    terrantula import terraform --state ./terraform.tfstate --config terrantula.yaml --replace --dry-run

    --replace deletes only entities previously synced from the same source that are no longer present. It won't touch entities from other sources.

A re-import or rescan didn't change anything

Symptom — you re-imported a source whose state changed, but the entity's properties in Terrantula didn't update.

Cause — the import source's reconciliation policy is human-approval. Under that policy a rescan does not mutate entities; it writes per-entity diffs into pending drift proposals for a human to approve. (New entities are still created directly — creation is non-destructive.) The other policy, auto-update (the default), applies incoming source state directly.

Fix

  • If you expect re-imports to apply automatically, confirm the source uses auto-update.
  • If the source is intentionally human-approval, approve the pending proposal to apply the diff (or reject to discard it). Proposals are surfaced per source via the import-sources drift rollup; approving applies the stored diff and re-stamps the entity.
  • A repeated rescan supersedes older pending proposals for the same entity, so a cron-driven rescan loop won't pile up zombie proposals.

The graph disagrees with my state

Symptom — an entity's properties, relationships, or lifecycle state in Terrantula don't match your Terraform.

Cause — the graph reflects the last ingested state. If your Terraform changed after the last import (someone applied a change, added/removed a resource), the graph is simply behind.

Fix — re-ingest. Pull fresh state and re-import:

terraform state pull > terraform.tfstate
terrantula import terraform --state terraform.tfstate --config terrantula.yaml

Do not try to fix the discrepancy by editing the entity in the dashboard — the UI is read-only and can't write entity properties, lifecycle states, or relationships. The correct direction is source → Terrantula (re-import) for observed state, and Terrantula → source → your CI (an Action's PR) for intended changes.

Keeping drift visible automatically

Symptom — you want drift surfaced continuously, not only when you remember to re-import.

Cause — Terrantula does not ship a built-in scheduler. Rescan is a plain HTTP endpoint your own scheduler calls.

Fix — wire any self-hosted scheduler (cron, a GitHub Actions / GitLab CI schedule, an Atmos workflow, a systemd timer) to re-read the source and re-import on an interval. Because import terraform / import-atmos register and refresh the import-source record on every run, a scheduled re-import gives you an updated drift rollup with no glue code. This path runs entirely against the Apache 2.0 backend — no SaaS dependency.

Rescan is observational — it never provisions

A rescan applies declarative entity state against Terrantula's catalog so the graph matches what the source reports. It never runs terraform apply. Actual infrastructure changes still flow through Actions → PRs → your CI.

Pre-existing entities don't appear in a drift rollup

Symptom — entities imported before import-source tracking existed don't show in a per-source drift rollup, even though they appear under GET /entities?driftStatus=….

Cause — those entities were sync-stamped but have no registered import-source row, so the per-source rollup (which matches on source_uri) skips them. The direct driftStatus filter still works.

Fix — re-run import terraform / import-atmos once against the same source. Registration is idempotent on (project, env, source URI) and backfills the row on the next run, after which the entities appear in the source's drift rollup.