Terraform’s graph command doesn’t just draw boxes and arrows; it’s a direct visualization of how your infrastructure components rely on each other, revealing the hidden choreography of your deployments.
Let’s see it in action. Imagine you have a simple Terraform configuration:
# main.tf
resource "aws_instance" "example" {
ami = "ami-0c55b159cbfafe1f0" # Example AMI ID
instance_type = "t2.micro"
tags = {
Name = "HelloWorld"
}
}
resource "aws_eip" "example" {
instance = aws_instance.example.id
vpc = true
}
When you run terraform graph, it generates a DOT language output that can be rendered by tools like Graphviz. The output will look something like this (simplified for clarity):
digraph {
compound = "true"
newrank = "true"
subgraph cluster_0 {
label = "root"
url = "#"
graph [root=true]
aws_instance.example [label = "aws_instance.example", shape = "box"]
aws_eip.example [label = "aws_eip.example", shape = "box"]
}
aws_instance.example -> aws_eip.example [label = "id", arrowhead = "vee", color = "blue", style = "solid"]
}
This DOT output can be piped to a rendering tool. For example, to generate a PNG image:
terraform graph | dot -Tpng > terraform.png
The resulting terraform.png will show aws_instance.example as a box, and an arrow pointing from it to aws_eip.example. The arrow is labeled "id" because the aws_eip resource’s instance argument explicitly depends on the id attribute of the aws_instance. This visual dependency is crucial.
The core problem Terraform solves is managing the complex, non-linear dependencies in infrastructure. Without a tool like Terraform, manually provisioning and orchestrating resources like this would involve intricate scripting, careful ordering of operations, and a deep understanding of each service’s creation and update lifecycle. terraform graph makes these implicit relationships explicit.
Internally, Terraform builds an execution plan by first constructing a dependency graph. It analyzes your configuration files, identifies all resources, and then traces the relationships between them based on how resource attributes are referenced. For instance, if a security group’s ID is used in a network interface’s configuration, Terraform knows the security group must be created before the network interface. The terraform graph command is essentially a way to expose this internal graph representation.
The levers you control are the resource definitions themselves. Any time you reference an attribute of one resource within the configuration of another, you are creating an explicit dependency that terraform graph will visualize. For example:
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
}
resource "aws_subnet" "example" {
vpc_id = aws_vpc.main.id # Dependency created here
cidr_block = "10.0.1.0/24"
}
Running terraform graph on this would show an arrow from aws_vpc.main to aws_subnet.example, labeled id, because vpc_id is set to aws_vpc.main.id.
The most surprising aspect for many is how terraform graph can reveal implicit dependencies that aren’t immediately obvious from the configuration syntax. For example, if a resource uses a provider configuration that is itself dependent on another resource (like a specific IAM role for an EC2 instance profile), terraform graph will depict this, even if the direct link isn’t a simple attribute reference. It models the entire dependency chain, including provider configurations and module outputs.
Understanding this graph is key to debugging and optimizing your Terraform code. If terraform graph shows a circular dependency, your configuration is fundamentally unresolvable. If it shows a resource you didn’t expect to be dependent on another, it might indicate an unintended coupling that could lead to issues during updates.
The next step after visualizing dependencies is understanding how Terraform traverses this graph during apply operations to determine the order of resource creation, updates, and destruction.