Debug cycle with azurerm_management_group parent_management_group_id

Wondering how I would go about doing the following:

provider "azurerm" {
  features {}
}

variable "management_groups" {
  type = list(object({
    display_name = string
    unique_name  = string
    parent       = optional(string, "")
  }))
  default = [{
    display_name = "Platform"
    unique_name  = "mg-platform"
    parent       = "mg-org"
    }, {
    display_name = "Connectivity"
    unique_name  = "mg-connectivity"
    parent       = "mg-platform"
    }, {
    display_name = "Identity"
    unique_name  = "mg-identity"
    parent       = "mg-platform"
  }]
}

locals {
  _core_management_group_map = {
    for management_group in var.management_groups : management_group.unique_name => management_group
  }
  _core_management_group_data = {
    # See: https://github.com/hashicorp/terraform/issues/22281#issuecomment-517080564
    for v in compact(setsubtract(distinct(values(local._core_management_group_map)[*].parent), distinct(values(local._core_management_group_map)[*].unique_name))) : v => v
  }
}

data "azurerm_management_group" "core_management_group" {
  # for every parent without a matching unique_name.
  for_each = local._core_management_group_data

  name = each.key
}

resource "azurerm_management_group" "core_management_group" {
  for_each = {
    for management_group in var.management_groups : management_group.unique_name => management_group
  }

  parent_management_group_id = each.value.parent != "" ? try(data.azurerm_management_group.core_management_group[each.key].id, azurerm_management_group.core_management_group[each.value.parent].id) : null
  display_name               = each.value.display_name
  name                       = each.value.unique_name
}

I can kind of work around this by first creating the parents but it would be nice if terraform could do a bit of probing of the actual depth of the cycle instead of just giving up or maybe my code is subpar?

Possibly because of the way the Terraform language evolved, there is no way to represent dependencies between different members of a single for_each collection.

All dependency relationships are between blocks, with for_each collections only being expanded comparatively late in processing when the relevant block is handled.

A consequence of this, is that Terraform cannot deal well with hierarchical containers all provisioned through a single resource with for_each.

When I look at your example code, I feel you may have fallen into a trap - that I personally would consider a Terraform antipattern - of trying to move all of the information about your resources into variables. In my opinion, the best approach here, unless the number of groups is huge, is to just write out individual data and resource blocks, one for each group, with no use of for_each.

If there is a hard requirement to take the set of groups from an external source, then I’d be looking to write a small script to consume that input and write out a .tf file using a templating language, to be run before invoking Terraform.