Terraform Internals: Dependency Graph

Visualize and manipulate Terraform's internal dependency graph engine.

Introduction

30 Second Summary

Every time you run terraform apply, Terraform quietly builds an internal map of your infrastructure, decides what can run at the same time, and picks an execution order you never see. When something breaks or takes forever, you are debugging blind.

In this project, you will make Terraform's invisible execution engine visible. You will render actual dependency graphs as images, prove parallel execution with timestamps, and deliberately break the graph to understand the constraints Terraform enforces.

What You'll Build

A laboratory of Terraform configurations where you can run a single command, open the resulting PNG, and see exactly which resources depend on which, then apply and watch timestamps prove that independent resources fire simultaneously.

By the end of this project, you'll have:

  • A rendered PNG of the exact Directed Acyclic Graph Terraform builds from your configuration, showing every node and edge.
  • Terminal output with timestamps proving parallel execution, showing how Terraform's walker launches independent resources at the same second.
  • Side-by-side graph images showing the difference between implicit and explicit dependencies, so you know exactly when to use depends_on.
  • A configuration that triggers a cycle error, proving why Terraform requires an acyclic graph and how to diagnose loops.
  • Secret Mission: Use -target to partially walk the graph and observe the consequences on state and subsequent plans.

Are there any prerequisites?

You should be comfortable with basic Terraform syntax (resource blocks, variables, outputs) and familiar with terminal commands. No cloud resources are created in this project, so no AWS credentials are needed.

Before We Start

Before We Start...

Before we dive in, let's lock in what you're about to build and why it matters.

In this project, you'll make Terraform's invisible dependency graph visible, prove how the parallel execution walker determines resource ordering with real timestamps, and deliberately break the graph to understand the constraints Terraform enforces.

Set Up Your Environment

Terraform builds a dependency graph from your configuration before it executes anything. To make that graph visible, you need two tools: a recent version of Terraform that supports the graph output options, and Graphviz to render the DOT-format output into an image you can open.

In this step, you'll upgrade Terraform to v1.15.8, install Graphviz, and create a project workspace for your laboratory configurations.

In this step, get ready to:
  • Upgrade Terraform CLI to v1.15.8.
  • Install Graphviz for rendering dependency graphs.
  • Create the terraform-graph-lab project workspace.
Upgrade Terraform to v1.15.8

This project uses features available in Terraform v1.15.8, the current stable release. Your existing installation is v1.5.7, so you need to upgrade before continuing.

  • Open your terminal by pressing Cmd+Space (macOS) or the Windows key (Windows), typing Terminal, and pressing Enter.
  • Check your current Terraform version by running this command:
terraform version

✔️ I see version 1.15.8 or higher

You already have the required version. Skip ahead to the next substep (Install Graphviz).

ⓧ I see an older version

Your Terraform is outdated. Upgrade it using the official Homebrew tap from HashiCorp.

  • Add the HashiCorp tap to Homebrew by running:
brew tap hashicorp/tap
  • Install the latest Terraform from the tap by running:
brew install hashicorp/tap/terraform

Seeing a linking conflict?

If Homebrew reports that terraform is already installed from a different source, run brew unlink terraform first, then re-run the install command above.

  • Confirm the upgrade succeeded by running:
terraform version

You should see v1.15.8 in the output.

Still seeing the old version?

Close your terminal and open a fresh one. Then run terraform version again. Some shells cache the path to binaries and need a new session to pick up the updated one.

help me upgrade Terraform to v1.15.8 using Homebrew

ⓧ Command not found

Terraform is not installed yet. Install it using the official Homebrew tap from HashiCorp.

  • Add the HashiCorp tap to Homebrew by running:
brew tap hashicorp/tap
  • Install Terraform from the tap by running:
brew install hashicorp/tap/terraform
  • Confirm the install succeeded by running:
terraform version

You should see v1.15.8 in the output.

Brew command not found?

You need Homebrew installed first. Visit brew.sh and follow the install instructions for macOS, then retry the commands above.

help me install Terraform on macOS

Install Graphviz

Graphviz is a graph visualization tool that reads DOT-format text and renders it into image files. Terraform outputs its dependency graph in DOT format, so Graphviz is what turns that text into a PNG you can actually look at.

  • Check if Graphviz is already installed by running:
dot -V

✔️ I see a version number

Graphviz is already installed. Skip ahead to the next substep (Create the project workspace).

ⓧ Command not found

Graphviz is not installed yet. Install it with Homebrew.

  • Install Graphviz by running:
brew install graphviz
  • Confirm the install succeeded by running:
dot -V

You should see the Graphviz version number printed in the output.

Install failed?

Make sure you have an active internet connection and that Homebrew itself is working. Run brew doctor to check for common issues.

help me install Graphviz on macOS

Create the project workspace

You need a dedicated folder for the Terraform configurations you'll build in this project. Every .tf file and generated graph image will live here.

  • Move to your Desktop by running this command:
cd ~/Desktop
  • Create your project folder and move into it by running these commands:
mkdir terraform-graph-lab
cd terraform-graph-lab
  • Open this folder in VS Code by running:
code .

What if code . doesn't work?

If your terminal says "command not found", open VS Code manually. Press Cmd+Shift+P inside VS Code, type Shell Command: Install, and select the option to install the code command in your PATH. Then close and reopen your terminal and try again.

  • Create the initial Terraform file by running:
touch main.tf
  • Confirm the file exists by running:
ls

You should see main.tf listed in the output.

Don't see main.tf?

Make sure you ran cd terraform-graph-lab before running touch main.tf. If you're in a different directory, the file was created there instead.

help me find or create main.tf in my project folder

Your environment is ready. Next up, you'll write your first Terraform configuration and see the raw dependency graph it produces.

See the Raw DAG

Your environment is ready. Now it is time to see what Terraform actually does before it touches any infrastructure.

Every time you run terraform plan or terraform apply, Terraform first builds a Directed Acyclic Graph (DAG) from your configuration. This graph determines execution order. In this step, you will make that invisible graph visible by rendering it as an actual image.

In this step, get ready to:
  • Write a configuration with three independent resources and render the graph.
  • Add dependency edges between resources and render the updated graph.
  • Compare both graphs to understand how Terraform determines execution order.
Write a configuration with three independent resources

The terraform_data resource is a built-in resource that does not require a provider and does not create real infrastructure. It is perfect for experimenting with graph mechanics.

  • In VS Code, open main.tf and paste the following configuration:
terraform {
  required_version = ">= 1.15.0"
}

# Three independent resources with no dependencies between them
resource "terraform_data" "alpha" {
  input = "alpha-value"
}

resource "terraform_data" "beta" {
  input = "beta-value"
}

resource "terraform_data" "gamma" {
  input = "gamma-value"
}

What does this code do?

Each terraform_data resource stores a simple input value in Terraform state. Because none of these resources reference each other, Terraform sees them as completely independent.

The required_version constraint ensures anyone running this configuration has a compatible Terraform version.

  • Save main.tf.
  • Initialize the working directory by running this command:
terraform init

You should see output ending with "Terraform has been successfully initialized!" confirming the setup is complete.

Seeing a version constraint error?

If Terraform reports that your version does not satisfy the >= 1.15.0 constraint, your CLI did not upgrade properly in Step 1. Run terraform version to check.

help me fix my Terraform version so it satisfies the >= 1.15.0 constraint

Now generate the dependency graph as a PNG image. This pipes Terraform's DOT output through Graphviz to render it.

  • Generate and open the graph by running these commands:
terraform graph -type=plan | dot -Tpng > graph.png
open graph.png

What does this command do?

  • terraform graph -type=plan outputs the plan-phase dependency graph in DOT format.
  • dot -Tpng takes that DOT text and renders it into a PNG image.
  • > graph.png writes the rendered image to a file in your project directory.
  • open graph.png opens the image in your default image viewer (Preview on macOS).

You should see three resource nodes (alpha, beta, gamma) each connecting only to a single root node. There are no edges between the resources because they are independent.

Image is blank or dot command not found?

If you see a "command not found" error for dot, Graphviz is not installed. Run brew install graphviz and try again.

If the image opens but looks blank, try resizing the window. Some renderings start very small.

help me troubleshoot my terraform graph rendering

Add a dependency chain between resources

With three independent nodes, Terraform could execute them in any order. Now you will create dependency edges by making one resource reference another's output.

  • In main.tf, find the beta resource. Find this line:
  input = "beta-value"
  • Replace it with a reference to alpha's output, so the line now looks like this:
  input = terraform_data.alpha.output
  • Now find the gamma resource. Find this line:
  input = "gamma-value"
  • Replace it with a reference to beta's output, so the line now looks like this:
  input = terraform_data.beta.output

What are implicit dependencies?

When one resource references another's attribute (like terraform_data.alpha.output), Terraform automatically creates a dependency edge in the graph. This is called an implicit dependency. Terraform knows it must create alpha before beta because beta needs alpha's output value.

  • Save main.tf.

Before you run the next command, what do you think the graph will look like now? Will the three nodes still be independent, or will you see directed edges connecting them?

  • Generate the updated graph by running these commands:
terraform graph -type=plan | dot -Tpng > graph-chain.png
open graph-chain.png

You should see a clear chain of directed edges: alpha points to beta, beta points to gamma. Each node also connects to the root. This is the dependency graph Terraform builds internally before any execution begins.

Graph still looks the same as before?

Make sure you saved main.tf before running the graph command. Terraform reads from disk, not from your editor buffer.

Also check that you opened graph-chain.png (not graph.png from earlier).

help me figure out why my graph-chain.png looks wrong

✔️ Awesome, I've got everything!

Great. Double check you have saved main.tf and that both graph.png and graph-chain.png exist in your project folder.

ⓧ I'd like to double check the full code

Here is the complete main.tf at this point:

terraform {
  required_version = ">= 1.15.0"
}

resource "terraform_data" "alpha" {
  input = "alpha-value"
}

resource "terraform_data" "beta" {
  input = terraform_data.alpha.output
}

resource "terraform_data" "gamma" {
  input = terraform_data.beta.output
}
Compare the two graphs

Open both images side by side to see the difference at a glance.

  • In the independent graph (graph.png): all three resource nodes connect only to root. They are parallel candidates.
  • In the chain graph (graph-chain.png): directed edges enforce a sequential ordering from alpha to beta to gamma.

Why does this matter?

Terraform determines execution order entirely from these edges, not from the order resources appear in your .tf file. You could reorder the resource blocks in main.tf and the graph (and therefore the execution plan) would be identical.

This is why Terraform uses a topological ordering of the DAG to decide what runs first, not line numbers.

You have rendered the static graph Terraform builds before execution. Next up, you will watch Terraform actually walk this graph at runtime and prove that independent nodes execute in parallel.

Watch the Parallel Walker

You've seen the static dependency graph. It shows structure, but it doesn't show behavior.

Terraform walks the graph at runtime using a concurrent executor that launches multiple resources simultaneously whenever the graph allows it. In this step, you'll add timestamps to each resource and prove that independent resources execute in parallel.

In this step, get ready to:
  • Add provisioner blocks that print start and end timestamps to each resource.
  • Apply with serial execution and observe sequential timestamps.
  • Re-apply with default parallelism and observe parallel timestamps.
Add provisioners that print timestamps

To observe execution order, each resource needs to announce when it starts and finishes. A provisioner "local-exec" block runs a shell command on your machine during apply. Adding a 2-second sleep makes timing differences clearly visible.

You also need to remove the dependency chain from Step 2 so all three resources become independent candidates for parallel execution.

  • Replace the entire contents of main.tf with this configuration:
terraform {
  required_version = ">= 1.15.0"
}

# All three resources are independent - no references between them
resource "terraform_data" "alpha" {
  input = "alpha-value"

  # Print timestamps to make execution order visible
  provisioner "local-exec" {
    command = "echo 'START alpha '$(date +%H:%M:%S) && sleep 2 && echo 'END alpha '$(date +%H:%M:%S)"
  }
}

resource "terraform_data" "beta" {
  input = "beta-value"

  provisioner "local-exec" {
    command = "echo 'START beta '$(date +%H:%M:%S) && sleep 2 && echo 'END beta '$(date +%H:%M:%S)"
  }
}

resource "terraform_data" "gamma" {
  input = "gamma-value"

  provisioner "local-exec" {
    command = "echo 'START gamma '$(date +%H:%M:%S) && sleep 2 && echo 'END gamma '$(date +%H:%M:%S)"
  }
}

What does this configuration do?

Each resource now has a static input value instead of referencing another resource's output. This removes all dependency edges between them.

The provisioner "local-exec" block runs a shell command that prints a START timestamp, sleeps for 2 seconds, then prints an END timestamp. This turns each resource into a stopwatch you can read in the terminal output.

  • Save main.tf.

✔️ Awesome, I've got everything!

Great. Double check you've saved your file before continuing.

ⓧ I'd like to double check the full code

terraform {
  required_version = ">= 1.15.0"
}

resource "terraform_data" "alpha" {
  input = "alpha-value"

  provisioner "local-exec" {
    command = "echo 'START alpha '$(date +%H:%M:%S) && sleep 2 && echo 'END alpha '$(date +%H:%M:%S)"
  }
}

resource "terraform_data" "beta" {
  input = "beta-value"

  provisioner "local-exec" {
    command = "echo 'START beta '$(date +%H:%M:%S) && sleep 2 && echo 'END beta '$(date +%H:%M:%S)"
  }
}

resource "terraform_data" "gamma" {
  input = "gamma-value"

  provisioner "local-exec" {
    command = "echo 'START gamma '$(date +%H:%M:%S) && sleep 2 && echo 'END gamma '$(date +%H:%M:%S)"
  }
}
Apply with serial execution

Terraform's -parallelism flag controls how many graph nodes the walker processes concurrently. Setting it to 1 forces Terraform to walk exactly one node at a time, regardless of what the graph allows.

  • Apply the configuration with serial execution by running:
terraform apply -auto-approve -parallelism=1

What does -parallelism=1 do?

The -parallelism flag sets the semaphore limit on concurrent operations. With -parallelism=1, Terraform processes one node at a time even when the graph has no dependency edges blocking parallel execution. The default value is 10.

Read the timestamps in the output. Each resource starts AFTER the previous one finishes. The total time is approximately 6 seconds (3 resources times 2 seconds each).

Seeing an error about existing state?

If you previously ran terraform apply in Step 2, run terraform destroy -auto-approve first to clean the state, then retry the apply command above.

help me fix a Terraform state conflict when applying my updated configuration

Destroy and re-apply with default parallelism

You just saw serial execution. Now you'll destroy the state and re-apply with Terraform's default parallelism of 10 concurrent operations. Since all three resources are independent (no dependency edges between them), the walker can launch all three at once.

  • Destroy the existing state by running:
terraform destroy -auto-approve

Before you re-apply, think about what you expect to see. If all three resources have no edges between them and the parallelism limit is 10, what should the START timestamps look like?

  • Apply with default parallelism by running:
terraform apply -auto-approve

Read the timestamps. All three resources START at nearly the same second. The total time is approximately 2 seconds, not 6.

What just happened?

With no dependency edges between the three nodes, Terraform's walker launched all three concurrently. They each slept for 2 seconds in parallel, so the total wall-clock time was about 2 seconds instead of 6.

The -parallelism flag controls the semaphore limit (how many nodes CAN run at once), but the graph structure determines which nodes are ELIGIBLE to run at once. Both constraints must be satisfied.

Timestamps still look sequential?

Make sure you removed the attribute references between resources. If beta still references terraform_data.alpha.output, Terraform sees a dependency edge and waits for alpha to finish first.

help me figure out why my Terraform resources are not executing in parallel

You've now seen the graph come alive at runtime. Independent nodes execute concurrently. Dependent nodes wait. Next up, you'll explore the two mechanisms Terraform uses to create those dependency edges in the first place.

Compare Implicit vs Explicit Dependencies

Terraform creates dependency edges two ways. Implicit dependencies form automatically when one resource references another's attributes. Explicit dependencies require the depends_on meta-argument.

These two types look different in the graph and have different use cases. Choosing the wrong one introduces subtle ordering bugs that only surface at apply time. In this step, you'll see both side by side and understand when each is appropriate.

In this step, get ready to:
  • Restore the implicit dependency chain and add a fourth resource.
  • Compare graph output with and without depends_on.
  • Confirm the execution order with timestamps.
Restore the implicit dependency chain and add a fourth resource

In the previous step, you removed the dependency chain to observe parallel execution. Now you'll restore those attribute references and add a fourth resource that has no connection to the chain.

  • In main.tf, find the beta resource block.
  • Find this line:
  input = "beta-value"
  • Replace it with a reference to alpha's output, so it looks like this:
  input = terraform_data.alpha.output
  • Now find the gamma resource block.
  • Find this line:
  input = "gamma-value"
  • Replace it with a reference to beta's output, so it looks like this:
  input = terraform_data.beta.output

These attribute references create implicit dependencies. Terraform sees that beta needs alpha's output and automatically draws an edge in the graph.

  • Add the following new resource block at the bottom of main.tf, below the gamma resource:
resource "terraform_data" "delta" {
  input = "delta-value"

  provisioner "local-exec" {
    command = "echo 'START delta '$(date +%H:%M:%S) && sleep 2 && echo 'END delta '$(date +%H:%M:%S)"
  }
}

What does this code do?

The delta resource has a plain string as its input value. It does not reference any other resource's attributes.

Because there's no attribute reference, Terraform has no reason to draw an edge from delta to anything else in the graph. Delta should float independently, just like all three resources did in Step 2.

  • Save main.tf.
  • Generate the graph by running this command:
terraform graph -type=plan | dot -Tpng > graph-implicit.png
  • Open the graph image by running:
open graph-implicit.png

You should see alpha, beta, and gamma connected in a chain (the implicit edges from your attribute references). Delta connects only to the root node, floating independently with no edges to the chain.

Don't see delta in the graph?

Make sure you saved main.tf before running the graph command. The terraform graph command reads from disk, not from your editor buffer.

Also confirm the delta resource block sits outside the gamma block's closing brace, not nested inside it.

help me debug why delta isn't showing in my terraform graph output

Add an explicit dependency with depends_on and compare

Delta currently has no reason to wait for gamma. But what if delta relies on a side effect that gamma produces, like a file written to disk or a permission applied? Terraform can't infer that from attribute references alone.

  • In main.tf, find the delta resource block.
  • Find this section:
resource "terraform_data" "delta" {
  input = "delta-value"

  provisioner "local-exec" {
  • Add a depends_on line between the input line and the provisioner block, so the top of the delta resource now looks like this:
resource "terraform_data" "delta" {
  input = "delta-value"

  depends_on = [terraform_data.gamma]

  provisioner "local-exec" {

What does depends_on do?

The depends_on meta-argument forces a dependency edge that Terraform cannot infer from data flow. It tells Terraform: "do not start delta until gamma has completely finished, even though I'm not reading any of gamma's attributes."

Use depends_on only for side effects Terraform cannot see. For example, an IAM policy must exist before a resource uses it, but the resource doesn't reference the policy's attributes directly.

  • Save main.tf.
  • Generate the updated graph by running:
terraform graph -type=plan | dot -Tpng > graph-explicit.png
  • Open the new graph by running:
open graph-explicit.png

Compare this to graph-implicit.png. You should see a new directed edge from delta to gamma, even though delta still doesn't reference any of gamma's attributes. That edge exists purely because of depends_on.

Both graphs look the same?

Double-check that your depends_on line sits inside the delta resource block (between the opening and closing braces), not outside it.

Also make sure you saved main.tf before regenerating the graph.

help me figure out why depends_on isn't creating a new edge in my graph

Apply and confirm the execution order with timestamps

The graphs show the static structure. Now you'll apply the configuration and watch the timestamps prove that the explicit edge enforces ordering at runtime.

  • Destroy the existing state to get a clean baseline by running:
terraform destroy -auto-approve

Before you apply, think about this: delta has a depends_on pointing to gamma, and gamma depends on beta, which depends on alpha. What order do you expect to see the START timestamps print in?

  • Apply the configuration by running:
terraform apply -auto-approve

Read the timestamps in your terminal output. Delta's START timestamp is always after gamma's END timestamp. The depends_on edge forced Terraform's walker to finish gamma before even beginning delta.

What would happen without depends_on?

Without the depends_on edge, delta would be a parallel candidate alongside alpha. Both would START at nearly the same timestamp because neither depends on the other in the graph.

The explicit edge you added is the only thing preventing that parallel execution. This is why depends_on matters for side-effect dependencies that Terraform cannot see from attribute references alone.

Seeing errors during apply?

If you see a cycle error, check that alpha's input is still a plain string ("alpha-value") and does not reference gamma or delta.

If provisioners fail, confirm each command value uses single quotes around the echo text and $(date +%H:%M:%S) for the timestamp.

help me debug my terraform apply errors in the dependency step

✔️ Awesome, I've got everything!

Great. Double check you've saved main.tf with all four resources in place.

ⓧ I'd like to double check the full code

Your main.tf should contain these sections in order. Scroll up through the step and confirm each one is present:

  • The terraform block with required_version = ">= 1.15.0".
  • The alpha resource with input = "alpha-value" and its provisioner.
  • The beta resource with input = terraform_data.alpha.output and its provisioner.
  • The gamma resource with input = terraform_data.beta.output and its provisioner.
  • The delta resource with input = "delta-value", depends_on = [terraform_data.gamma], and its provisioner.

You can now read a Terraform graph and predict exactly which resources will wait for others. Next up, you'll deliberately break the graph to understand what happens when Terraform can't find a valid execution order.

Break the Graph with Cycle Detection

You have seen how Terraform builds a DAG and walks it in parallel to execute resources. But what happens when the graph has no valid execution order?

A cycle means resource A depends on B, which depends on C, which depends back on A. No topological ordering exists for that loop, so Terraform cannot determine which resource to create first. In this step, you will deliberately create a circular dependency to see exactly how Terraform detects and reports it.

In this step, get ready to:
  • Trigger a cycle error by creating a circular dependency between resources.
  • Visualize the cycle using the -draw-cycles flag.
  • Fix the cycle and confirm the graph is valid again.
Create a circular dependency

Right now, your dependency chain flows in one direction: alpha feeds beta, which feeds gamma. You will close the loop by making alpha depend on gamma's output, creating an impossible cycle.

  • In VS Code, open main.tf and find the terraform_data.alpha resource block at the top.
  • Find this line:
  input = "alpha-value"
  • Replace "alpha-value" with terraform_data.gamma.output, so the line now looks like this:
  input = terraform_data.gamma.output

This creates a loop: alpha depends on gamma (via this reference), gamma depends on beta (via its input), and beta depends on alpha (via its input). No valid start point exists.

  • Save main.tf.

Before you run the next command, what do you think Terraform will report when it tries to build the graph for this configuration?

  • Run terraform validate to check the configuration:
terraform validate

Terraform prints an error starting with Error: Cycle: followed by the resource addresses forming the loop. It refuses to proceed because no valid execution order exists.

Why does Terraform reject cycles?

Terraform's execution engine uses a topological sort to determine the order of operations. A topological sort only works on acyclic graphs. If a cycle exists, the sort algorithm has no starting point and cannot produce a valid ordering.

This validation happens before any resources are created or modified. Terraform catches the problem at plan time, not at apply time, so you never end up with a half-built infrastructure state.

Visualize the cycle with -draw-cycles

Seeing the error message tells you a cycle exists. Seeing the graph shows you exactly which edges form the loop. The -draw-cycles flag highlights cycle edges with colored lines in the DOT output.

  • Render the graph with cycle highlighting by running this command:
terraform graph -draw-cycles -type=plan | dot -Tpng > graph-cycles.png
  • Open the generated image:
open graph-cycles.png

You should see the resource nodes with the cycle edges highlighted in a different color from the normal dependency edges. The colored edges trace the exact loop: alpha to beta to gamma back to alpha.

When is -draw-cycles useful?

In a small configuration like this one, the cycle is obvious from reading the code. In large configurations with dozens of modules and outputs, cycles can form accidentally through indirect references that span multiple files.

The -draw-cycles flag is a diagnostic tool for those complex situations. It only works when -type is also specified.

Not seeing the image?

Make sure you included both -draw-cycles and -type=plan in your command. The -draw-cycles flag requires -type to be set.

If open does not work on your system, try opening the file directly from VS Code's Explorer sidebar or your file manager.

help me render my Terraform graph with cycle highlighting

Fix the cycle and verify the graph

Now that you have seen what a cycle looks like, restore the configuration to its valid state.

  • In main.tf, find the terraform_data.alpha resource block again.
  • Find this line:
  input = terraform_data.gamma.output
  • Replace terraform_data.gamma.output with "alpha-value", so the line returns to this:
  input = "alpha-value"
  • Save main.tf.

What do you expect terraform validate to report now that the circular reference is gone?

  • Run validation to confirm the fix:
terraform validate

You should see a success message with no errors. The graph is acyclic again.

  • Generate the fixed graph to confirm the structure by running:
terraform graph -type=plan | dot -Tpng > graph-fixed.png
  • Open the fixed graph:
open graph-fixed.png

You should see the familiar acyclic structure: alpha feeds beta, beta feeds gamma, and delta depends on gamma via depends_on. No colored cycle edges remain.

Where do accidental cycles come from?

In real projects, cycles rarely happen from directly referencing resources in a loop. They usually emerge when module outputs feed back into module inputs across boundaries, or when depends_on is overused and accidentally creates a hidden loop.

Now you know how to diagnose them: run terraform validate to confirm a cycle exists, then render the graph with -draw-cycles to trace the exact edges causing the problem.

Still seeing a cycle error after fixing?

Make sure you changed the input line in the alpha resource block specifically, not in beta or gamma. Alpha's input should be the string "alpha-value", not a reference to another resource.

help me fix a cycle error in my Terraform configuration

✔️ Awesome, I've got everything!

Great. Confirm you have saved main.tf and that terraform validate passes with no errors.

ⓧ I'd like to double check the full code

Your main.tf should contain these four resource blocks in order. Scroll through your file and confirm each one matches:

  • The terraform block with required_version = ">= 1.15.0".
  • Resource alpha with input = "alpha-value" and its local-exec provisioner.
  • Resource beta with input = terraform_data.alpha.output and its local-exec provisioner.
  • Resource gamma with input = terraform_data.beta.output and its local-exec provisioner.
  • Resource delta with input = "delta-value", depends_on = [terraform_data.gamma], and its local-exec provisioner.

You have now seen the hard boundary Terraform enforces on your configurations. The DAG must be acyclic, or nothing runs. Next up, you will put everything together in a Secret Mission that explores what happens when you walk only part of the graph.

Secret mission

Partial Graph Walks with -target

The -target flag tells Terraform to walk only a subgraph. Target a single resource, observe what the walker includes (and excludes), then see the consequences on state and subsequent plans.

Clean Up Your Resources

Clean Up Your Resources

This project runs entirely locally. All terraform_data resources exist only in local Terraform state, so there are no ongoing cloud costs.

Decide whether to keep your resources for reference, pause them to come back later, or delete them entirely.

Resources you used:

  • The terraform-graph-lab project directory (contains main.tf, graph PNG files, .terraform directory, and terraform.tfstate).
  • Graphviz (installed via Homebrew for rendering DOT output to PNG).
  • Local Terraform state tracking four terraform_data resources (alpha, beta, gamma, delta).

Keep everything running

No action needed. Choose this if you want to keep the project as a reference for future dependency debugging.

  • Your terraform-graph-lab directory stays on your Desktop with all .tf files and rendered graph PNGs intact.
  • Graphviz remains installed. You can use dot to render DOT output from any future Terraform project.
  • Terraform 1.15.8 remains your active version for all projects.

Pause - I'll come back to this later

There are no running processes or cloud resources to pause. Your project is already inert on disk.

  • The Terraform state file and configuration will be exactly where you left them when you return.
  • To pick up where you left off, open the terraform-graph-lab folder in VS Code and run any terraform command.

Delete - I don't want to use this again

Remove all project files and clean up installed tools you no longer need.

  • Destroy the Terraform state by running this command from inside your project directory:
terraform destroy -auto-approve
  • Delete the project directory by running:
cd ~/Desktop
rm -rf terraform-graph-lab
  • If you no longer need Graphviz for other projects, uninstall it by running:
brew uninstall graphviz

What about Terraform itself?

This project upgraded your Terraform from 1.5.7 to 1.15.8. If you use Terraform for other projects, keep it installed. If you want to remove it entirely, run:

brew uninstall hashicorp/tap/terraform

Only do this if you are certain you no longer need Terraform on your machine.

That's a Wrap!

That's a Wrap!

Nice work! You have just built a Terraform laboratory that makes the invisible execution engine visible.

You can now look at any Terraform configuration and mentally reconstruct the dependency graph, predict which resources will execute in parallel, and diagnose ordering issues by visualizing the DAG.

What you learned:

  • Rendered the actual directed acyclic graph Terraform builds internally using terraform graph and Graphviz, seeing resource nodes and dependency edges as a visual PNG.
  • Proved that Terraform's concurrent walker executes independent resources in parallel using timestamps and the -parallelism flag, then distinguished between implicit dependencies (attribute references) and explicit dependencies (depends_on).
  • Triggered and diagnosed a cycle error, understanding why Terraform requires an acyclic graph and how to use -draw-cycles to highlight the problem.
  • Secret Mission: Used -target to walk a subgraph, observed incomplete state, and understood why partial applies are dangerous in production.

Ready to quiz yourself?