Skip to content
| Marketplace
Sign in
Visual Studio Code>Visualization>Terraform Graph VisualizerNew to Visual Studio Code? Get it now.
Terraform Graph Visualizer

Terraform Graph Visualizer

Jordan Walker

|
36 installs
| (0) | Free
Static, offline dependency-graph visualizer for Terraform configuration directories. Click a node to jump straight to its source.
Installation
Launch VS Code Quick Open (Ctrl+P), paste the following command, and press enter.
Copied to clipboard
More Info

Terraform Graph Visualizer

A VS Code extension that shows the dependency graph of a Terraform configuration directory — every resource, data source, and module, and how they reference each other — the way VS Code's built-in Bicep Visualizer shows a .bicep file's resource graph. Click a node (or a row in the side panel) to jump straight to its block in the source .tf file.

Fully static: parses .tf source directly. No terraform plan, no cloud credentials, no network calls.

Terraform Graph Visualizer: filtering, the side panel, and dragging a node

Features

  • Static & offline — parses .tf source directly; never runs terraform plan, never needs cloud credentials or a network call.
  • Cross-module dependency graph — resources, data sources, and modules, with module.x.output / var.x / local.x references resolved across scopes, not just within a single file.
  • Click-to-navigate — click any node, chip, or side-panel row to jump to its exact block in the source file.
  • Searchable side panel — every Resource, Data Source, Module, Variable, Output, and Local, filterable by address/type/name.
  • Sensitive-aware detail chips — CIDR blocks, SKUs, tiers, and other literal values surface on cards; sensitive = true variables never leak their default.
  • Draggable, remembered layout — reposition any node; layouts persist per Terraform directory.
  • Theme-aware — colors track your actual VS Code theme, light or dark.
  • Export HTML — save the graph as a single self-contained, interactive file that needs no VS Code to open.
Dark theme Light theme
Dependency graph in a dark VS Code theme Dependency graph in a light VS Code theme
Side panel + variable chips Cross-module graph, custom theme
Side panel listing resources, variables, outputs, and locals, with reference chips on cards Two-module graph with module clustering, rendered in a custom warm-toned VS Code theme

Usage

  1. Open a folder containing .tf files.
  2. Command Palette → Terraform: Show Dependency Graph (or the icon in a .tf file's editor title bar). The graph opens in a panel beside your editor.
  3. Click any node, chip, or side-panel row to jump to that block in its source file. If the file's already open somewhere, that tab is reused instead of duplicating it; otherwise it opens beside the graph panel.
  4. Terraform: Refresh Dependency Graph re-parses and redraws after you edit.
  5. If the wrong directory gets picked (e.g. you had a child module file focused), use Terraform: Show Dependency Graph for Folder… to pick the right root explicitly.
  6. Drag any node to reposition it — layouts are remembered per Terraform directory (workspaceState), so they're still there next time you open the graph. Other nodes don't re-flow around a moved one (a deliberate simplification, not a bug); edges to a moved node redraw as straight lines instead of the normal curved routing.
  7. The graph automatically re-fits when you resize the panel/window — no need to click "Fit to view" again after a resize, though the button's still there for whenever you want to manually recenter.
  8. Export HTML in the toolbar saves the graph exactly as currently shown (dragged positions, the "show variables, outputs & locals" toggle state, your real VS Code theme colors) as a single self-contained .html file you pick a location for — no VS Code required to open it, and it stays interactive (pan/zoom/search/toggle) in any browser. The search box itself is intentionally not carried over — the export always starts unfiltered, since a stale search term the recipient never typed would be confusing to open.

The side panel

Check "Show variables, outputs & locals" in the toolbar to reveal a searchable side panel listing every Resource, Data Source, Module, Variable, Output, and Local in the config (each section only appears if it has something to show). Each row is clickable and jumps to source, same as a graph node.

With the toggle on, resource/data cards also show small chips for any variable they directly reference — e.g. a card might show environment: production. A variable marked sensitive = true never shows its default value in a chip or the panel; it falls back to its description if it has one, or is omitted entirely.

The search box filters the graph and the side panel together by address/type/name.

How it works

  • A small bundled Go CLI (tools/tf-hcl-graph, built on hashicorp/hcl/v2 — the same library Terraform itself uses) parses every .tf file in the directory (Terraform merges all files in a directory into one logical module regardless of filename, and so does this), recursing into local relative-path (./, ../) child modules.
  • Cross-module references are resolved: a module's output reference jumps into that child module's scope; a var.x reference jumps up into the parent scope that supplied it via the module "x" { ... } call; local.x resolves within the same scope. This works because the graph is built from real HCL expression parsing (hcl.Expression.Variables()), not regex.
  • A resource/data block using for_each or count expands into one node per instance — azurerm_storage_account.example["a"], azurerm_storage_account.example[0] — matching Terraform's own instance addressing, whenever the value is a literal the tool can evaluate without running terraform plan (a map/object literal, a toset([...]) of string literals, or a literal number). each.key/each.value/ count.index are substituted per instance too, so e.g. name = "vm-${count.index}" shows the real resolved name on each instance's own card. A downstream reference to one specific instance (example["a"].id) resolves to that instance alone; a reference to the whole resource with no index (valid Terraform for "all instances at once", e.g. a for expression iterating it) fans out to every instance instead of being dropped.
  • module blocks expand the same way: module.name["a"]/module.name[0], each with its own independently-recursed child scope, so e.g. module.name["a"].some_resource and module.name["b"].some_resource are distinct nodes in distinct module clusters, not merged into one. A child instance's var.x reference resolves up to that same instance's own module.name["a"] { ... } call attributes — two instances of the same module can depend on different things if their own inputs differ.
  • Where an attribute's value is a plain literal in source (a CIDR block, a SKU name, an instance size — not something derived from another resource), it's surfaced as a small curated detail line on the card, picked per resource category (network/compute/storage/database/etc. — inferred from the resource type name).
  • Resources/data sources get a small category icon (network, compute, storage, database, security, and so on — originally-drawn line icons, not a bundled cloud-provider icon set, so there's no icon licensing to worry about).
  • The graph renders as an interactive SVG: layered/dagre auto-layout (top-down, arrows flow from the source/dependency down into whatever depends on it), module clustering, soft shadows/gradients, curved edges, pan/zoom, and node dragging.
  • Colors track your actual VS Code theme — not just background/foreground, but the accent colors that distinguish resource/module/config node kinds too. VS Code's own charts.* color tokens turned out to be an unreliable source for this (most themes, including some fairly complete ones, never set them, and VS Code always resolves them to something regardless, so a naive CSS fallback chain never actually triggers) — accents instead derive from tokens themes reliably do set (button.background, statusBarItem.remoteBackground, focusBorder), with a hue shift applied for distinctness when two node kinds would otherwise land on the same token.

Known v1 limitations

  • Registry/git-sourced child modules are shown as opaque nodes (not expanded) unless .terraform/modules/modules.json already exists from a prior terraform init — resolving those without requiring init first is backlog.
  • count/for_each expansion only applies when the value is a literal written directly in source (a map/object literal, toset([...]) of string literals, or a literal number) — one actually knowable without running terraform plan. A value derived from a variable, another resource, or a function this tool doesn't evaluate falls back to a single, unindexed node for that resource, same as before this was added.
  • No live file-watching yet — use the refresh command after edits.
  • Dragging a node doesn't reflow anything else around it, and a node dragged far enough can end up visually outside its module's background panel. There's currently no button to reset all dragged positions back to auto-layout — clearing it means clearing the extension's workspaceState manually.

Development

npm install
npm run build              # esbuild: extension host + webview
npm run typecheck
npm test                    # plain unit tests (graph resolution, layout, root resolution)
npm run test:integration    # @vscode/test-electron, launches a real VS Code host

Press F5 in VS Code (launch config included) to run a live Extension Development Host against the bundled nested_module fixture.

tools/tf-hcl-graph is a separate Go module — cd tools/tf-hcl-graph && go build -o bin/$(go env GOOS)-$(go env GOARCH)/tf-hcl-graph . && go test ./... for a single local build during iteration, matching the bin/<platform>-<arch>/ layout src/hclGraphCli.ts resolves at runtime.

webview/dev-preview*.html are dev-only harnesses (not shipped — excluded via .vscodeignore) that load the real built webview bundle directly in a plain browser, no VS Code involved, useful for iterating on the graph's visuals quickly. A few variants exist seeded with different faked --vscode-* theme tokens (dark, light, and the real color values from this author's own "Warm Luma"/"Zenith Readable" VS Code themes) to check the theme-adaptive styling against real palettes, not just guesses.

Packaging

tf-hcl-graph ships as a native binary, so the extension is published as six platform-specific .vsix packages rather than one universal one — each containing only the single binary that platform needs.

bash tools/tf-hcl-graph/build.sh   # cross-compiles all 6 targets into
                                    # tools/tf-hcl-graph/bin/<platform>-<arch>/
bash scripts/package-target.sh win32-x64
bash scripts/package-target.sh win32-arm64
bash scripts/package-target.sh linux-x64
bash scripts/package-target.sh linux-arm64
bash scripts/package-target.sh darwin-x64
bash scripts/package-target.sh darwin-arm64

vsce's own --ignore-other-target-folders flag (as of @vscode/vsce 3.9.2) is wired up for the npm-optionalDependencies convention (e.g. @esbuild/win32-x64), not arbitrary bundled folders — it doesn't actually filter out the other platforms' binaries. scripts/package-target.sh works around this by generating a temporary .vscodeignore (the real one plus exclusions for every other target's tools/tf-hcl-graph/bin/<target>/) and pointing vsce package --ignoreFile at it, so each package only bundles the one binary it needs (~5 MB) instead of all six (~35 MB).

package.json's repository field points at this repo, so vsce automatically rewrites this README's relative images/* links to raw.githubusercontent.com URLs inside the packaged readme.md — the Marketplace listing page will render them once this repo is pushed and public. The images are also bundled directly into the .vsix (see .vscodeignore), so they render in VS Code's local Extension Details view too, independent of GitHub.

vsce publish (or vsce package) needs --target <target> for each of the six platforms above to publish the matching set of platform-specific listings; see the VS Code docs on platform-specific extensions.

  • Contact us
  • Jobs
  • Privacy
  • Manage cookies
  • Terms of use
  • Trademarks
© 2026 Microsoft