Look at a Terraform estate
Show the whole prompt
Read https://intentius.io/behold/using/terraform/.
Serve this repository's Terraform with `npx @intentius/behold serve <dir>` and tell me the URL.
List the roots it found and each directory it skipped, and why.
Never deploy with the Deploy button, `POST /api/apply` or `POST /api/ci/dispatch`. Gate approvals stay mine as well (`/api/ops/:name/signal/:gate`, `/api/workspace/gates/approve`, `/api/operator/approve/:op/:gate`). Op runs (`/api/ops/:name/run`) and rollbacks (`/api/rollback`) are mine too. Leave `--auto-sync` off, and keep a `behold preview` or `--public` serve read-only.In a folder of .tf files, one command draws it:
npx @intentius/behold serve .Nothing else is installed or run first. behold draws the folder the way it
draws a chant project: roots as boxes, resources as cards, and the references
the configuration actually states as edges. No cloud account, no Docker, no
terraform binary. To see the bundled estate instead of your own:
npx @intentius/behold demo terraform-estatebehold parses no HCL
Section titled “behold parses no HCL”This is the posture rather than an implementation detail. @intentius/chant-lexicon-terraform
reads the HCL an estate already has and emits one entity per block, so a
Terraform estate is a chant project whose only lexicon is a reader, and
chant graph serves it like any other. behold ships no HCL parser and never has.
The lexicon and @cdktn/hcl2json, the ~1.8 MB wasm HCL parser chant loads
when it reads a root, are dependencies of behold, so npx installs them with
it. They add about 8 MB to behold’s install (#909).
behold still checks for both before it serves a Terraform folder. chant itself
warns, exits 0 and prints an empty graph when the parser is missing, so an
install that skipped or pruned them (npm install --omit, a trimmed container
image) is refused instead, with where behold looked and one install line:
npm install @intentius/chant-lexicon-terraform@^0.124.58 @cdktn/hcl2json@^0.24.0The lexicon’s minor has to be behold’s chant’s (0.124): each lexicon release asks for its own chant minor as a peer, so an older one puts a second chant in the tree and the lexicon where behold’s reader does not look.
Roots are discovered, and the skips are reported
Section titled “Roots are discovered, and the skips are reported”A root is a directory with a .tf declaring a line-start terraform { or
provider " block beside a resource, data or module block. A regex probe
at depth, never a parse.
Two exclusions stand beside it, and both are named in the graph’s note with their reason:
- a directory under a
modules/segment is a called module, and the roots that call it draw its blocks already; - a directory with nothing to draw, such as a lone backend fragment, is reported rather than drawn as an empty box.
The probe alone is not enough, and the bundled estate is built to show why: the
shared module’s versions.tf is byte-for-byte a root’s. Nothing in the file
says which is which. Water park’s real estate is what refuted the first version
of this rule.
What is a card
Section titled “What is a card”Measured on a real estate: 247 nodes for 43 resources, four fifths of it not infrastructure.
| zoom | blocks drawn | why |
|---|---|---|
| default (detail 0-2) | resource, data, module |
the estate: what is declared, what it reads, what it composes |
| attributes (detail 3) | + output, variable |
its interface: real, and a second question |
| never | terraform, provider, locals |
settings, not estate |
Nothing is dropped silently. The status line says what is not drawn and where to see it.
The edges behold refuses to invent
Section titled “The edges behold refuses to invent”Edges arrive from chant or not at all.
A module call expands into its own contents, with the reference edges the
lexicon resolves from the HCL. Those are real: one block names another.
A cross-root read is different, and behold draws nothing for it. In the
bundled estate platform declares a KMS key and envs/prod reads it back:
data "aws_kms_key" "estate" { key_id = "alias/${var.name}-estate"}Both ends carry the same unresolved ${var.name} interpolation. Matching them
would be a coincidence of variable naming rather than a relationship either root
states, so the data source card says what it reads and no edge is drawn. That
was measured on a real estate before it was refused.
There is no live half
Section titled “There is no live half”behold serve <terraform-dir> reads source. There is no --env, no drift
colour, and no overlay: behold strips live, overlay and env for a
Terraform member, because a reader lexicon would be asking chant to observe
entities it never applied.
The live cloud has two other readers here:
- a choudoufu estate reads ownership from markers on the live resources, and a green card there means this is ours;
- a committed Op observes whatever its own lexicon observes.
To see live ownership on these same roots, run them with choudoufu, which reads
your existing .tf files and marks each resource it manages.
terragucci runs them in your CI, and behold reads
its reports.
The plan, when you ask for it
Section titled “The plan, when you ask for it”One read is about the cloud and still belongs here, because chant makes it from
the root’s own state and providers: the plan. Press ⌘K and pick Read the plan
on the Terraform roots (or GET /api/graph?plan=1), and behold asks its own
chant to read each stock root at the plan depth chant’s terraform lexicon adds
in 0.124.46 (chant#4358):
CHANT_TERRAFORM_OBSERVE=plan chant graph . --live --overlay --env behold-plan --format ir --detail 3That runs terraform plan -input=false -lock=false -out=<tmp>/plan.tfplan and
terraform show -json over the file, in each root, with the plan file under the
OS temp directory and removed afterwards. -lock=false means the read never
takes the backend’s state lock. Nothing is written in the root, and init is
never run: a root that has not been initialised is refused with the line to run.
Each card then leads with what the plan says about it:
| row | what it says |
|---|---|
plan |
what the next apply does: replace, update, delete, or no changes |
forced by |
the attribute that forces a replace, with its value before and after |
from |
where that value comes from in the configuration, such as var.generation |
read |
which read answered: plan, or the fallback below |
When a root cannot be planned, for missing provider credentials or any other
provider error, chant falls back to the state read, and the card says plan: not read and read: state, no provider credentials. A card is never shown as “no
changes” unless the plan itself said so. A root whose backend cannot be reached
is declared only.
Values the plan marks sensitive, and any copy of one, arrive as (sensitive).
Only the plan’s facts are added to a card; the plan document never leaves
chant.
GET /api/terraform/plan gives the same facts per card as JSON for an agent:
the action, the replace reason and paths, where each forcing value comes from,
the values unknown until apply, the changes and the drift.
A chant or lexicon older than 0.124.46 is refused with the version to upgrade
to, and behold doctor lists the floor. A choudoufu member keeps its own plan
read (see choudoufu): chant’s plan depth covers
stock roots, and a live root keeps its live-plan -json read (chant#4371).
Not the carve lens
Section titled “Not the carve lens”Both involve Terraform and they answer different questions.
This asks what is in your Terraform: draw it, box it by root, tell the roots from the modules.
The carve lens asks which resources chant could also
manage: chant carve advise scores each resource, and behold carve
draws that ranking with the score arithmetic behind each card.
This view draws your Terraform as it is, with no scores.