Using a for_each loop in a resource that joins 2 other resources created by loops

Hi All,

I’m pretty new to terraform and IaC. I’m hoping this something that I just haven’t been able to find; but, I feel like I’ve been all of the forums and github.

I’m attempting to write a module for Cisco ACI that will create the contract, contract subject, filter, and filter entry.

The contract subject resource “joins” the contract by the contract ID and the filter (which contains the filter entry) by the filter ID.

In my code I’m attempting to create a variable file that is easy to read like an AWS NSG would be.

However, if I use for_each to create the contracts and the filters then I cannot loop on either the contract or filter to get the IDs of the other. See the resource aci_contract_subject with relation_vz_rs_subj_filt_att and contract_dn.

module main.tf

terraform {
  required_providers {
    aci = {
      source = "CiscoDevNet/aci"
      version = "1.0.1"
    }
  }
}

resource "aci_contract" "aci_contract" {
  for_each = var.var_aci_contract
  tenant_dn  = each.value.tenant_dn
  name       = each.value.name
  description = "${each.value.d_from_port}-${each.value.d_to_port}"
}

resource "aci_filter" "contract_filter" {
    for_each = var.var_aci_contract
    tenant_dn  = each.value.tenant_dn
    name = "allow-${each.value.name}"
}

resource "aci_contract_subject" "aci_contract_subject" {
    for_each = aci_contract.aci_contract
    name = "${aci_contract.aci_contract[each.key].name}-SUBJ"
    contract_dn = aci_contract.aci_contract[each.key].id
   relation_vz_rs_subj_filt_att = aci_filter.contract_filter[each.value].id
}

resource "aci_filter_entry" "aci_filter_entry" {
   for_each = var.var_aci_contract
   name = each.value.name
   filter_dn = aci_filter.contract_filter[each.key].id
   ether_t = each.value.ether_t
   prot = each.value.prot
   stateful = each.value.stateful
   d_from_port = each.value.d_from_port 
   d_to_port = each.value.d_to_port
}

module variables.tf

variable "var_aci_contract" {
    type = map(object({
        tenant_dn = string
        name = string
        ether_t = string
        prot = string
        stateful = string
        d_from_port = string
        d_to_port = string
    }))
}

aci.tf

module "aci_contract_mod" {
  source = "./modules/aci_contract"
  var_aci_contract = {
    "app1" = {
      name = "https1"
      ether_t = "ipv4"
      prot = "tcp"
      stateful = "yes"
      d_from_port = "443"
      d_to_port = "443"
      tenant_dn = data.aci_tenant.aci_tenant.id
      }
    "app2" = {
      name = "https2"
      ether_t = "ipv4"
      prot = "tcp"
      stateful = "yes"
      d_from_port = "444"
      d_to_port = "444"
      tenant_dn = data.aci_tenant.aci_tenant.id
      }
}
}

How can I loop through these to create the individual resources and join them both with indexed IDs?

In case any one stumbles across this specific issue with ACI I found the best way to get around this issue is to create the filter and filter entries with the contact.

Then loop the contract_subject on the contract and that way you can call all the ID objects created from w/i the contract.

This is a proof of concept that did work and ugly.

resource "aci_contract" "aci_contract" {
  for_each = var.var_aci_contract
  tenant_dn  = each.value.tenant_dn
  name       = each.value.name
  description = "${each.value.d_from_port}-${each.value.d_to_port}"
  filter {
    filter_name = "allow-${each.value.name}"
    filter_entry {
      filter_entry_name = each.value.name
      ether_t = each.value.ether_t
      prot = each.value.prot
      stateful = each.value.stateful
      d_from_port = each.value.d_from_port 
      d_to_port = each.value.d_to_port
    }
  }
}

resource "aci_contract_subject" "aci_contract_sub" {
    for_each = aci_contract.aci_contract
    name = "${aci_contract.aci_contract[each.key].name}-SUBJ"
    contract_dn = aci_contract.aci_contract[each.key].id
    relation_vz_rs_subj_filt_att = [aci_contract.aci_contract[each.key].filter[0].id]
}