How to create multiple objects with a single resource

I’m very new to Terraform so I might be using wrong terminology, but I have a resource object that links a set of network/vlan encapsulation blocks (blocks have a from and to number) to another resource object.

The resource is pretty simple, looks like this:

resource "aci_ranges" "Physical-Encap-Block" {
  depends_on = [
    aci_vlan_pool.Phys-VLAN-Pool,
  ]
  vlan_pool_dn  = aci_vlan_pool.External-VLAN-Pool.id
  _from = some_number
  to = some_number
}

I’m trying to figure out if there’s a way I can link all the encapsulation blocks under a single resource instead of creating a new resource for every single encapsulation block.

So for example if I have three encapsulation blocks:
block1 = {from:2, to:5}
block2 = {from:12, to:15}
block3 = {from:22, to:25}

The blocks are known ahead of time so I’m ok with defining them as variables, I just can’t figure out how to roll it all up into a single resource. I’ve been looking at documentation and examples for for_each, but I can’t figure out how to use it for this specific use case.

Thanks.

Hi @aj-cruz,

The key requirement for for_each is that we build a map that has one element per instance we want to create. The keys of that map will be used by Terraform as identifiers for the individual instances. You used block1, block2, block3 in your example so I’m going to use those as examples here.

The first step is to declare the variable that the module caller will use to specify these “blocks”. It sounds like all we need here is a pair of numbers for each block, so we could represent this either as a two-element tuple or as an object with from and to attributes. I’m going to go with the latter because subjectively I think it’ll make the result easier to understand for a future reader:

variable "physical_encap_blocks" {
  type = map(object({
    from = number
    to   = number
  }))
}

In some cases with for_each we then need to transform the variable value to meet the requirements for for_each, which we’d often do using for expressions, but we’re fortunate in this case that our variable’s type already meets the requirement of being a map with one element per instance and so we can just use it directly with no further transformation:

resource "aci_ranges" "physical_encap_block" {
  depends_on = [aci_vlan_pool.phys_vlan_pool]
  for_each   = var.physical_encap_blocks

  vlan_pool_dn = aci_vlan_pool.external_vlan_pool.id
  from         = each.value.from
  to           = each.value.to
}

With a map like the one you showed in your initial collection, this would declare three instances with the following addresses:

  • aci_ranges.physical_encap_block["block1"]
  • aci_ranges.physical_encap_block["block2"]
  • aci_ranges.physical_encap_block["block3"]

I must caveat that I’m not familiar with the aci provider at all, so I’ve just guessed what it needs here based on what you shared in your example. It might need some adjustments if this resource has some additional constraints that are not clear from your example. If you run into problems and you’re not sure how to proceed, please let me know and I’ll try to figure out how to adjust it.

Thank you I appreciate the reply. The provider resource actually takes a string for the variables so I changed it a bit, here’s what I tried based on your example:

variable "physical_encap_blocks" {
  type = map(object({
    from = "vlan-2"
    to   = "vlan-2"
  }))
}

resource "aci_ranges" "Physical-Encap-Block" {
  depends_on = [aci_vlan_pool.Phys-VLAN-Pool]
  for_each = var.physical_encap_blocks
 
  vlan_pool_dn  = aci_vlan_pool.External-VLAN-Pool.id
  _from = each.value.from
  to = each.value.to
}

But Terraform gives me this:

Error: Invalid type specification

  on aci.tf line 67, in variable "physical_encap_blocks":
  67:     from = "vlan-2"

A type specification is either a primitive type keyword (bool, number, string)
or a complex type constructor call, like list(string).

Error: Invalid type specification

  on aci.tf line 68, in variable "physical_encap_blocks":
  68:     to   = "vlan-2"

A type specification is either a primitive type keyword (bool, number, string)
or a complex type constructor call, like list(string).

For reference a successful task that actually creates the resource properly looks like this:

resource "aci_ranges" "External-Encap-Block" {
  depends_on = [
    aci_vlan_pool.External-VLAN-Pool,
  ]
  vlan_pool_dn  = aci_vlan_pool.External-VLAN-Pool.id
  _from = "vlan-101"
  to = "vlan-101"
}

I’m running Terraform version 0.12.25

Hi @aj-cruz,

Terraform is returning this error because "vlan-2" is a value, not a type. The correct way to write the type constraint you intended here would be:

variable "physical_encap_blocks" {
  type = map(object({
    from = string
    to   = string
  }))
}

Thank you, makes sense. So where do I define the actual blocks?
I tried adding:

default = {
from = “vlan-2”
to = “vlan-2”
}

to the variable but I get:

This default value is not compatible with the variable’s type constraint:
element “to”: object required.

Not sure how to provide the blocks and stuff that into the object.

The variable is defined as being a map of objects, rather than just a single object, so the default value must have an extra nesting level to represent the surrounding map. For example:

  default = {
    block1 = {
      from = 2
      to   = 5
    }
    block2 = {
      from = 12
      to   = 15
    }
    block3 = {
      from = 22
      to   = 25
    }
  }

Though the usual reason to declare a variable is if you intend the calling module to override the value, so depending on your goals it might be better to put that in the module block that calls your current module, or in a .tfvars file if you are writing a root module.

Awesome that did it thank you! I thought I had tried that structure but I think I was using it with a different approach (not with map). I’ve been trying so much stuff to get this to work.
I’m a network engineer, I have no problem picking up networking syntax, I’ve been working with Python for the past few years with no problem, but for some reason this Terraform syntax is kicking my butt. It’s like hieroglyphics to me, the map function is especially confusing to me.