Terraform-Generated Infrastructure Diagrams with draw.io

March 09, 2026  3 minute read  

Terraform-Generated Infrastructure Diagrams with draw.io

One recurring problem in infrastructure documentation is diagram drift.

The architecture diagram in documentation says one thing. Terraform state says another. The deployed environment says something else entirely.

Over time diagrams stop being trusted.

Recently I started experimenting with an idea to reduce this drift:

Generate architecture diagrams automatically from Terraform module outputs.

This is still a work in progress, but the early prototype is promising and worth sharing.


The Core Idea

Terraform modules already know what infrastructure they create.

So instead of manually drawing architecture diagrams, we can generate them directly from Terraform outputs.

The flow looks like this:

Terraform Apply
      │
      ▼
terraform output -json
      │
      ▼
Renderer Script
      │
      ▼
Generated draw.io diagrams

Result:

generated/
 ├─ overview.drawio
 └─ modules/
      ├─ network.drawio
      └─ aks.drawio

Instead of manually updating diagrams, they become derived artifacts of infrastructure code.


Design Goals

The approach is built around a few principles.

1. Eliminate Diagram Drift

The diagram reflects actual deployed infrastructure, because the values come directly from Terraform outputs.

No manual updates required.


2. Module Ownership

Each Terraform module owns its own diagram template.

Example module structure:

modules/
  network/
    main.tf
    outputs.tf
    diagram/
      overview-layer.xml.tpl
      standalone.xml.tpl

This keeps documentation close to the infrastructure definition.


3. Composable Architecture Views

Each module produces two diagram artifacts:

  1. Standalone module diagram
network.drawio
aks.drawio
  1. Layer for a shared platform overview
overview.drawio

The overview diagram composes all modules together into a system view.


4. Reusable Modules

Because diagram templates live inside modules, they travel with the module.

If another team reuses a module, they automatically inherit its documentation.


Terraform Module Diagram Contract

Each module exports structured metadata describing what should appear in the diagram.

Example:

output "diagram" {
  value = {
    module_name = "network"
    title       = "Network"

    layout = {
      x = 80
      y = 80
    }

    resources = {
      vnet_name   = azurerm_virtual_network.this.name
      subnet_app  = azurerm_subnet.app.name
      subnet_data = azurerm_subnet.data.name
    }
  }
}

This becomes the contract between Terraform and the diagram renderer.

The renderer does not inspect Terraform state directly. It only consumes these outputs.


Template-Based Diagrams

Each module contains a draw.io template fragment.

Example file:

modules/network/diagram/overview-layer.xml.tpl

Example template snippet:

<mxCell id="_vnet"
        value="VNet: "
        style="rounded=1;whiteSpace=wrap;html=1;"
        vertex="1"
        parent="_group">
  <mxGeometry x="20" y="50" width="260" height="60" as="geometry"/>
</mxCell>

Tokens like this:


are replaced with Terraform output values during rendering.


Rendering the Diagrams

After Terraform runs:

terraform output -json > outputs.json
python render_drawio.py outputs.json

The renderer performs three simple steps:

  1. Load Terraform outputs
  2. Replace tokens inside templates
  3. Assemble the final draw.io document

Resulting Architecture View

The generated diagram groups modules visually.

Example conceptual layout:

+--------------------+      +---------------------+
| Network            |      | AKS                 |
|                    |      |                     |
| VNet               |─────▶| Kubernetes Cluster  |
| Subnets            |      | Node Resource Group |
+--------------------+      +---------------------+

Each module becomes a self-contained block that can be reused or moved independently.


Why draw.io?

draw.io (diagrams.net) works well for generated diagrams because:

  • widely used
  • simple XML format
  • easy to generate programmatically
  • still editable visually when needed

This makes it a good compromise between automation and flexibility.


Current Limitations

This approach is still experimental and intentionally simple.

XML Templates Are Verbose

draw.io XML is not particularly pleasant to maintain.

A future version may replace XML templates with a simpler YAML-based specification.


Layout Is Static

Modules currently define fixed coordinates:

layout = {
  x = 80
  y = 80
}

Eventually layout could be automated.


Cross-Module Relationships

Modules do not create edges to other modules directly.

Relationships are currently defined separately to avoid tight coupling between modules.


Future Directions

If the approach proves useful, several improvements are possible.

Shared Style Library

Standard icons and styles for common infrastructure components.


YAML Diagram Specification

Instead of raw XML templates:

nodes:
  - id: vnet
    label: "VNet: "
    x: 20
    y: 50

Automatic Layout

Modules could declare relationships rather than absolute coordinates.


CI/CD Integration

Pipelines could automatically publish diagrams after infrastructure deployment.


Final Thoughts

Infrastructure has increasingly become fully code-driven:

  • infrastructure
  • networking
  • security policies
  • deployment pipelines

But diagrams are often still maintained manually.

Generating diagrams directly from Terraform modules could help keep architecture documentation closer to reality.

This is still an early prototype, but the idea of diagrams-as-code owned by Terraform modules looks promising.

More updates as the experiment evolves.

Leave a comment