How to reference instance argument value created with for_each meta-argument in another instance in the same map

Original question is asked here.

My end goal is to have Terraform create instances of a resource generated with the for_each meta argument in a specific sequence. HCL is known to be a declarative language and when Terraform applies a configuration it can create resources randomly unless you use the depends_on argument or refer from one resource (instance) to another. However, the depends_on argument does not take values that are “calculated”, so I don’t know how to use it in modules.

For this reason, in order to force Terraform to create instances of a resource in a specific sequence, I decided to try to make the value of a certain argument in an instance it creates “calculated” based on the values of the same argument from another instance.

Below you can find a more practical example.

Let’s take a test module that instantiates the cloudflare_page_rule resource:

# Module is placed to module\main.tf

terraform {
  experiments = [module_variable_optional_attrs]
}

terraform {
  required_providers {
    cloudflare = {
      source  = "cloudflare/cloudflare"
      version = ">= 3.10.0"
    }
  }
}

variable "zone" {
  type        = string
  description = "The DNS zone name which will be added, e.g. example.com."
}

variable "page_rules" {
  type = list(object({
    page_rule_name = string
    target         = string
    actions = object({
      forwarding_url = optional(object({
        url         = string
        status_code = number
      }))
    })
    priority   = optional(number)
    status     = optional(string)
    depends_on = optional(string)
  }))
  description = "Zone's page rules."
  default     = []
}

//noinspection HILUnresolvedReference
locals {
  page_rule_dependencies = { for p in var.page_rules : p.page_rule_name => p.depends_on if p.depends_on != null }
}

# https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/zone
resource "cloudflare_zone" "this" {
  zone = var.zone
}

# https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/page_rule
//noinspection HILUnresolvedReference
resource "cloudflare_page_rule" "this" {
  for_each = var.page_rules != null ? { for p in var.page_rules : p.page_rule_name => p } : {}

  zone_id = cloudflare_zone.this.id

  target = each.value.target
  actions {
    //noinspection HILUnresolvedReference
    forwarding_url {
      status_code = each.value.actions.forwarding_url.status_code
      url         = each.value.actions.forwarding_url.url
    }
  }
  priority = each.value.depends_on != null ? cloudflare_page_rule.this[local.page_rule_dependencies[each.key]].priority + 1 : each.value.priority
  status = each.value.status
}

output "page_rule_dependencies" {
  value = local.page_rule_dependencies
}

And a configuration that is used to create resources:

terraform {
  required_version = ">= 0.15.0"

  required_providers {
    cloudflare = {
      source  = "cloudflare/cloudflare"
      version = ">= 3.10.1"
    }
  }
}

variable "cloudflare_api_token" {
  type      = string
  sensitive = true
}

provider "cloudflare" {
  api_token = var.cloudflare_api_token
}

module "acme_com" {
  source = "./module"
  zone   = "acme.com"
  page_rules = [
    {
      page_rule_name = "page_rule_1"
      target         = "acme.com/url1"
      actions = {
        forwarding_url = {
          status_code = 301
          url         = "https://www.example.com/url1"
        }
      }
      priority = 1
    },
    {
      page_rule_name = "page_rule_2"
      target         = "acme.com/url2"
      actions = {
        forwarding_url = {
          status_code = 301
          url         = "https://www.example.com/url2"
        }
      }
      priority   = 2
      depends_on = "page_rule_1"
    },
    {
      page_rule_name = "page_rule_3"
      target         = "acme.com/url3"
      actions = {
        forwarding_url = {
          status_code = 301
          url         = "https://www.example.com/url3"
        }
      }
      priority   = 3
      depends_on = "page_rule_2"
    }
  ]
}

output "page_rule_dependencies" {
  value = module.acme_com.page_rule_dependencies
}

In this particular example, I’ve added the depends_on argument to the page_rules variable (don’t confuse this argument with the depends_on meta argument). For the value of the depends_on argument, I specified the name of a page_fule on which another page_fule depends.

Next, I created a local variable page_rule_dependencies, the value of which, after calculations, is the following (you can check this yourself by replacing the priority = each.value.depends_on != null ? cloudflare_page_rule.this[local.page_rule_dependencies[each.key]].priority + 1 : each.value.priority construct with priority = each.value.priority and executing terraform apply):

page_rule_dependencies = {
  "page_rule_2" = "page_rule_1"
  "page_rule_3" = "page_rule_2"
}

Further, in the priority = each.value.depends_on != null ? cloudflare_page_rule.this[local.page_rule_dependencies[each.key]].priority + 1 : each.value.priority construct, I refer to the values ​​of the local variable, thereby forming a “reference” to the page_fule instance, on which the current instance depends:

  1. When creating page_rule_1, the value of its argument priority = 1.
  2. When creating page_rule_2, the value of its argument priority = cloudflare_page_rule.this["page_rule_1"].priority + 1.
  3. When creating page_rule_3, the value of its argument priority = cloudflare_page_rule.this["page_rule_2"].priority + 1.

However, I get an Error: Cycle: module.acme_com.cloudflare_page_rule.this["page_rule_3"], module.acme_com.cloudflare_page_rule.this["page_rule_2"], module.acme_com.cloudflare_page_rule.this["page_rule_1"] error.

Either I’m doing something wrong, or it’s some kind of Terraform limitation/bug. Is there a way to get rid of this error?

P.S. Resulting graph after terraform graph -draw-cycles | dot -Tsvg > graph.svg or terraform graph -draw-cycles -type=plan | dot -Tsvg > graph-plan.svg (the same result):

P.P.S. I use Terraform v1.1.7.

As you say yourself Terraform really isn’t designed to do this at all, so while you might be able to hack something that works I’d not be confident that at any point it might break. Instead I’d either look at using a tool which is designed for programmatic changes (rather than defined state) such as Ansibe or write something around Terraform that uses -target to do things in order (or split into multiple state files for the same reason).

@stuart-c I still hope there is a solution. To be honest, I don’t understand why this shouldn’t work.