linuxlab.io
Tutorials▾
  • Linux & networking
    File system, processes, TCP/IP, BGP and OSPF
    →
  • Terraform & IaC
    HCL, state, plan/apply on a LocalStack sandbox
    →
  • Git & GitHub
    Object model, plumbing, branching, GitHub Actions
    →
All tutorials →
PricingAboutSign inCreate account
/
Intro
Lessons
Footer
linuxlab-TutorialsPricingAboutPrivacy & cookies
Copyright © 2026 LinuxLab. All rights reserved.
linuxlab.io
Tutorials▾
  • Linux & networking
    File system, processes, TCP/IP, BGP and OSPF
    →
  • Terraform & IaC
    HCL, state, plan/apply on a LocalStack sandbox
    →
  • Git & GitHub
    Object model, plumbing, branching, GitHub Actions
    →
All tutorials →
PricingAboutSign inCreate account
/
  • Introduction
  • Lessons
  • How it works
  • Knowledge base
  • Cheat sheet
  • Capstone
  • Interview prep
home/terraform/lessons/tf-garden-01-cycle-error

lesson ── terraform-garden ── ~18 мин ── 3 шагов

Troubleshooting Garden: untangle the Cycle Error

In ~/tf-garden there is ready-made HCL. terraform init will pass, plan will not: Cycle Error. One resource points at another, that one at a third, the third points back at the first. Without untangling this, the graph cannot be built and plan will not run.

Your job is to find the cycle, understand why it is there, break it so the meaning stays intact, and prove with plan that the graph is now a DAG.

▶ интерактивный sandbox

Поднимется пара контейнеров: terraform 1.9 и localstack 3.8 в одной сети. В браузере откроется терминал, можно сразу terraform init. Каждый шаг проверяется автоматически. TTL 45 минут, без регистрации.

запустить sandbox →

stack ── terraform · localstack · 1 GB RAM · самоуничтожается через 45 мин простоя

Шаги

  1. 01

    Run init and plan, catch the Cycle Error

    Enter the directory, initialize, and try to run plan.

    bash
    cd /home/student/tf-garden
    terraform init
    terraform plan 2>&1 | tee /tmp/cycle.log

    In cycle.log you will see a block like this:

    Error: Cycle: random_id.suffix, aws_s3_bucket.demo, aws_s3_bucket_policy.demo

    Write down the three resource names. That is your cycle.

    ✓ Cycle confirmed. Now, why it is there.

    Where cycles come from

    The most common case: keepers or triggers in a utility resource points at a resource that itself depends on the utility resource. Here random_id.keepers.bucket_arn = aws_s3_bucket.demo.arn, while aws_s3_bucket.demo.bucket is garden-cycle-${random_id.suffix.hex}. That gives you a dependency in both directions.

    The second common case: depends_on added by hand on top of implicit deps. Terraform builds the graph automatically, and if you also write depends_on into the same place, you can end up with "A depends on B, B depends on A through a manual depends_on".

  2. 02

    Rewrite the HCL so the cycle disappears

    The fix: drop the back-dependency in random_id.keepers (it is useless, because random_id is recreated through -replace anyway), and move the condition out of the policy into locals:

    hcl
    locals {
      suffix_hex = random_id.suffix.hex
    }
    resource "random_id" "suffix" {
      byte_length = 4
    }
    resource "aws_s3_bucket" "demo" {
      bucket = "garden-cycle-${local.suffix_hex}"
    }
    resource "aws_s3_bucket_policy" "demo" {
      bucket = aws_s3_bucket.demo.id
      policy = jsonencode({
        Version = "2012-10-17"
        Statement = [{
          Effect    = "Allow"
          Principal = "*"
          Action    = "s3:GetObject"
          Resource  = "${aws_s3_bucket.demo.arn}/*"
          Condition = {
            StringEquals = {
              "aws:RequestTag/SuffixHex" = local.suffix_hex
            }
          }
        }]
      })
    }

    Put this into ~/tf-garden/main.tf over the old file. Afterwards, check that there is no keepers block left.

    подсказка

    The new main.tf must not contain the word `keepers`. Do not try to "fix" the policy by adding depends_on, that is the wrong direction.

    ✓ HCL rewritten. Now check the graph.

  3. 03

    Plan should pass and show a diff

    bash
    cd /home/student/tf-garden
    terraform plan -out=plan.tfplan

    Now plan should pass: "Plan: 3 to add, 0 to change, 0 to destroy". Apply is not required in this lesson, what matters is that the graph built.

    If plan still complains, look at terraform graph -draw-cycles, there may be one more cycle you did not notice.

    ✓ Plan built, the graph is now a DAG. Cycle untangled.

    The same thing on OpenTofu

    OpenTofu repeats the same Cycle check 1:1, with the same messages and the same graph command. This comes from the shared HCL heritage, it is not a difference between implementations. The full article on the differences and compatibility is tf-opentofu-parity.

    • → OpenTofu parity
    • → Common errors
    • → terraform graph

Что ты узнал

A cycle is always a design mistake, not a tf bug. See a cycle? Someone wrote dependencies in both directions. A random_id.keepers that points at a resource which itself depends on the same random_id is the classic case. The fix: remove the back-reference and move the logic into a data block or a local, or accept that the dependency goes one way.

команды

  • terraform graph -draw-cycles | dot -Tsvg > /tmp/g.svgdraws the cycle visually; if dot is not available, read the ASCII output
  • terraform graph -type=plan-destroyanother view of the same graph; sometimes the cycle is clearer this way

концепции

  • · A Cycle Error is always specific: a loop of 2 to 3 nodes, not "the whole graph is bad"
  • · random_id.keepers is a strong tool, but it triggers dependencies in both directions
  • · Conditions in policies can be computed with locals instead of attribute references

← предыдущий

Hello, S3: your first resource in Terraform

следующий →

Your first module: move S3 into something reusable

Footer
linuxlab-
Copyright © 2026 LinuxLab. All rights reserved.
Tutorials
Pricing
About
Privacy & cookies