Terraform-Generated Infrastructure Diagrams with draw.io
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:
- Standalone module diagram
network.drawio
aks.drawio
- 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:
- Load Terraform outputs
- Replace tokens inside templates
- 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