Terraform Guide

OpenTofu architecture diagram: one picture that survives the fork

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

OpenTofu repositories ask the same question Terraform ones do, and get the same unsatisfying answer. tofu graph is there, it works, and what it hands back is a DOT file of the dependency resolver. The useful move is the same on both sides of the fork: stop asking the binary and read the HCL, because the architecture is declared in the source and the source is identical.

6 min readFor teams running OpenTofu, or halfway through moving to it

See it as a diagram

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

141/20003 credits left
Try:

No account needed · Editable canvas, not a picture

What tofu graph is good at, and what it is not for

tofu graph writes the dependency graph in DOT format for Graphviz. Its -type flag selects plan, plan-refresh-only, plan-destroy, or apply; -draw-cycles colours the edges forming a cycle; -plan renders from a saved plan file instead of the configuration. For its actual job, debugging a cycle or checking that a depends_on landed where you meant it, nothing beats it.

As documentation it fails for three reasons that have nothing to do with OpenTofu. It graphs the resolver, so providers, data sources, and meta-nodes all become boxes; Terraform's own documentation for the equivalent command says the richer graph types expose implementation details of the language runtime. It weighs every node the same, so a random_id sits at the size of your GKE cluster. And it runs against one working directory with its root variables supplied, so a repository holding nineteen root modules is nineteen separate runs and nineteen DOT files that nobody assembles.

The fork changed the binary, not the parse surface

OpenTofu kept HCL2 and the block grammar it inherited: resource, data, module, variable, output, locals, terraform, provider. Directory layout, module sources, tfvars, the meaning of count and for_each, all the same. A parser that reads the source therefore does not need to know which binary you run.

Datadef parses every file ending in .tf and .tfvars in the connected repository: stacks, module tree, resources with their multiplicity, data lookups, variables followed through call sites and locals to literal values, references resolved through module outputs to the resources they actually reach. Modules become zones, major resources keep their provider icons, repeated minor types roll up with counts, and role assignments and grants are counted in a note rather than drawn.

Two OpenTofu specifics are worth stating plainly. The first is the .tofu extension: OpenTofu reads both .tf and .tofu, and where both exist with the same base name in a directory it prioritises the .tofu file and ignores the .tf one. Datadef fetches .tf and .tfvars, so in that override case it parses the file OpenTofu is ignoring. Keep shared configuration on .tf and the two readings agree.

The second is early variable evaluation, which lets an OpenTofu module source be built from a variable; the tofu graph documentation flags the same feature when it notes that variables in module sources need root variable values supplied on the command line. A source that is not a literal string cannot be resolved by a parser either, so pin registry sources as plain literals if you want the source and version printed on the box.

State encryption is a non-event here

OpenTofu encrypts state and plan files at rest, locally and in a backend. Since the analysis never reads either one, never runs init, and never touches your cloud, encryption changes nothing about what the diagram can do. See how repository sync works.

Migrating from Terraform without churning the diagram

A migration commit is loud in the diff and silent in the architecture. It swaps required_version, points required_providers at a different registry host, sometimes renames a lock file. None of that changes what the platform is.

Datadef hashes the parsed draw plan rather than the commit sha, so a sync whose structure fingerprint is unchanged skips generation entirely. The migration commit lands, the diagram does not move, and the first regeneration happens when somebody actually adds a module. For a mixed estate mid-migration, the generated architecture doc lists the providers in play and the state backend per stack, which is usually the fastest way to see which repositories have already been converted.

Connect from GitHub, GitLab, or Azure DevOps with read-only access, pick a branch or a tag, and the diagram regenerates daily and on demand. It embeds in a README or a wiki page as a live image and exports as PNG or JPEG.

FAQ

Does Datadef work with OpenTofu repositories?

Yes. OpenTofu uses the same HCL language, the same block types, and the same file layout as Terraform, and the analysis reads the source files rather than running a binary. An OpenTofu repository is parsed exactly the way a Terraform one is: stacks, modules as zones, resources with provider icons, and references resolved through module outputs.

How is this different from running tofu graph?

tofu graph outputs the dependency resolver graph in DOT format and needs an initialized working directory. It draws every provider, data source, and meta-node at equal weight, which is right for debugging cycles and wrong for documentation. A parsed architecture diagram keeps module structure as zones, curates minor resources into counts, and needs no init, no state, and no cloud credentials.

Are files with the .tofu extension parsed?

No. The sync fetches files ending in .tf and .tfvars only. OpenTofu treats .tofu as an override: where main.tf and main.tofu both exist in a directory it uses the .tofu file and ignores the .tf one, so in that case the parse reads the file OpenTofu is ignoring. Repositories that keep shared configuration on .tf are unaffected.

We are migrating from Terraform to OpenTofu. Will the diagram change?

Not from the migration itself. Changing required_version, pointing required_providers at a different registry host, or renaming a lock file leaves the parsed structure identical, and a sync whose structure fingerprint is unchanged stops before generation. The diagram stays exactly as it is until somebody adds a module or rewires two of them, which is the change worth redrawing for.

Does OpenTofu state encryption prevent the analysis?

No. OpenTofu encrypts state and plan files at rest, and the analysis reads neither. It parses the .tf and .tfvars files of the connected repository with no init and no backend access, so encrypted state, remote state, and a state currently held under a lock are all irrelevant to whether a diagram can be produced.