Terraform Guide

What belongs in an infrastructure diagram, and what belongs in the code

By the engineer who builds Datadef, from client work on data platforms · Reviewed August 21, 2026

Two failure modes, one cause. The diagram that shows nine boxes labelled Frontend, Backend, Database tells a reader nothing they did not assume. The diagram that shows all 123 declared resources tells them everything and communicates nothing. Both come from having no rule about what a node is for, so here is one, plus what to do with the detail it excludes.

6 min readFor anyone arguing in review about whether a resource deserves a box

See it as a diagram

Everything below, as a diagram you can edit. Describe yours and see it in seconds.

193/20003 credits left
Try:

No account needed · Editable canvas, not a picture

The rule: a node earns its place by changing a decision

If knowing that a thing exists changes what a reader would do, it is architecture. If it does not, it is inventory. A database earns a box because you route around it, size it, back it up, and worry about it. A role assignment that grants that database access to a managed identity changes no reader decision on its own, so it is inventory: real, necessary, not a box.

Applied consistently the rule produces four keepers. Things that hold state (databases, storage, queues, catalogs). Things that serve or compute (clusters, functions, gateways, workspaces). Boundaries (network, resource group, module, account), because boundaries are where permission and blast radius change. And direction, because an architecture diagram without arrows is an inventory with a layout.

Datadef applies that rule as a fixed classification rather than a judgment call, so the same repository produces the same exclusions every time. A resource type whose name contains role, permission, grant, policy_attachment, member, iam, entitlement, or acl is wiring. So is anything matching secret, certificate, key_vault, kms, or credential. So is every resource of the random, null, local, time, tls, archive, template, and external providers, which is why a random_id suffix never becomes a box. On a real azurerm or AWS platform repo those categories outnumber the infrastructure.

Counted, not deleted

The objection to leaving things out is that the diagram becomes incomplete. The answer is to count instead of delete, so nothing disappears silently. Datadef does this deterministically during the parse rather than leaving it to taste: major resources stay individual nodes with their provider icons; a repeated minor type inside a module collapses into one node carrying the count and the member names, like Storage credentials x2: external, root; and glue is summarized in a note that says, for example, 18 wiring resources not worth boxes: 8 grants, 6 permissions, 4 role assignments.

The three numbers from the repository this curation was tuned against are worth stating, because they are the whole argument. 123 declared resources. 62 left once wiring is excluded, which is what a first completeness pass drew, and which produced a correct and unreadable phone-book column. About 36 once repeated types are rolled up.

There are hard budgets behind it, and they run in two passes. The first pass draws major resources individually, rolls repeated minor types into one node each, and lets a module keep up to eight nodes before its remaining singles fold into a Supporting resources node carrying the count. If the canvas still exceeds the target of forty nodes, the whole plan is rebuilt in a strict pass where lone minors fold too. The target rises for repositories made of many small independent stacks: above eight stacks it becomes five nodes per stack, capped at seventy-two. The 123-resource platform repo this was built against lands at roughly 36 nodes, which is a diagram a person reads in one sitting.

DRAW    databases, storage accounts, clusters, workspaces, gateways,
        queues, catalogs, VPCs and resource groups as boundaries

COUNT   repeated minor types (subnets x6, storage credentials x2),
        overflow singles as "Supporting resources x9"

NOTE    matched by name: role, permission, grant, policy_attachment,
        member, iam, entitlement, acl, secret, certificate, kms,
        credential; plus every random_*, null_*, tls_* resource

TABLE   the full inventory, one row per module, in the generated doc

Where the excluded detail goes

Nothing should be excluded without a destination. Inputs and outputs go to terraform-docs. The full inventory goes to the generated module reference table, one row per module with source and resource count, which sits in the same architecture doc as the diagram. Naming conventions and unmanaged resources go to the README, because they are not in the code at all.

Keeping the diagram and the inventory in the same generated artifact is what makes the exclusion safe: a reader who wants the missing role assignment scrolls down rather than opening a repo. See what a docs-as-code pipeline can and cannot hold for the trade-off in the other direction.

FAQ

Should IAM roles and policies appear in an architecture diagram?

Not as boxes. Identity wiring changes no reader decision on its own and routinely outnumbers real infrastructure in a Terraform repository, so it belongs in a counted note and in the full inventory table. The exception is a diagram whose subject is the permission model itself.

How many boxes should an infrastructure diagram have?

Around forty is a practical ceiling for a single canvas that a person reads in one sitting, with more allowed when a repository is made of many small independent stacks. Past that, group repeated resources into counted nodes rather than shrinking the boxes.

Does an audit require every resource to be drawn?

Auditors ask for completeness of the record, not completeness of the picture. A curated diagram plus a generated inventory table listing every module and its resource count satisfies that, and it is far easier to review than a graph with several hundred equal-weight nodes.

Do tags and naming conventions belong on the diagram?

No. A convention applied uniformly adds the same noise to every node and distinguishes none of them, so it costs canvas space and returns nothing. State it once in the README, where a reader looks for rules rather than for structure. The exception is a tag that is not uniform, such as an owner or a cost centre that genuinely differs between components, which is worth an annotation on the nodes that differ.

Should one diagram cover all environments?

Usually yes, with the differences annotated on the nodes that differ. Environments built from one codebase share a shape, so three near-identical pictures cost three times the maintenance and hide the only thing worth looking at: where they diverge. That is normally a short list of counts, SKUs, and a backup or failover resource that exists in production alone. Split only when the topology itself differs.