Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Terraform Loop with Range

Tags:

terraform

Is there a way to loop through a range or list for a variable in terraform.

Below is what I would like to accomplish but I am not sure how to do it.

module "vlan_list" {
  depends_on  = [module.vlan_pools]
  source      = "../modules/add_vlans"
   vlan_list = {
      for i in range(1,100): {
        "access" = {
          vlan_pool   = module.vlan_pools.vlan_pool["access"]
          from        = i
          to          = i
        }
      }
   }
}

Let me add more information to it because unfortunately that didn't work. I get:

Error: Invalid value for module argument

  on pools_vlan.tf line 34, in module "vlan_list":
  34:    vlan_list = {
  35:       for i in range(1, 100):
  36:         "access" => {
  37:           vlan_pool   = module.vlan_pools.vlan_pool["access"]
  38:           from        = i
  39:           to          = i
  40:         }...
  41:    }

The given value is not suitable for child module variable "vlan_list" defined
at ../modules/add_vlans/variables.tf:5,1-21: element "access": object
required.

So I have created a module with the following

resource "aci_ranges" "add_vlan" {
  for_each      = local.vlan_list
  alloc_mode    = each.value["alloc_mode"]
  annotation    = each.value["annotation"]
  name_alias    = each.value["name_alias"]
  vlan_pool_dn  = each.value["vlan_pool"]
  role          = each.value["role"]
  from          = "vlan-${each.value["from"]}"
  to            = "vlan-${each.value["to"]}"
}

From Here I have defined a variables file to make it so users don't have to enter every variable... they can accept defaults

terraform {
  experiments = [module_variable_optional_attrs]
}

variable "vlan_list" {
  description = "Add VLANs to VLAN Pools"
  type = map(object({
    alloc_mode  = optional(string)
    annotation  = optional(string)
    from        = optional(number)
    name_alias  = optional(string)
    role        = optional(string)
    to          = optional(number)
    vlan_pool   = optional(string)
  }))
}

locals {
  vlan_list = {
    for k, v in var.vlan_list : k => {
      alloc_mode  = coalesce(v.alloc_mode, "static")
      annotation  = (v.annotation != null ? v.annotation : "")
      from        = (v.from != null ? v.from : 1)
      name_alias  = (v.name_alias != null ? v.name_alias : "")
      role        = coalesce(v.role, "external")
      to          = coalesce(v.to, 1)
      vlan_pool   = (v.vlan_pool != null ? v.vlan_pool : "")
    }
  }
}

So what I shared above is what someone would enter to consume the module... Here is a more complete example that I would like to do:

module "vlan_list" {
  depends_on  = [module.vlan_pools]
  source      = "../modules/add_vlans"
   vlan_list = {
      for i in range(1, 100):
        "access" => {
          vlan_pool   = module.vlan_pools.vlan_pool["access"]
          from        = i
          to          = i
        }...
      for i in ranges([1000-1200], [1300-1400]):
        "vmm_dynamic" => {
          alloc_mode  = "dynamic"
          vlan_pool   = module.vlan_pools.vlan_pool["vmm_dynamic"]
          from        = i
          to          = i
        }...
      for i in list[4, 100, 101]:
        "l3out" => {
          vlan_pool   = module.vlan_pools.vlan_pool["l3out"]
          from        = i
          to          = i
        }...
   }
}

I know the second range on the vmm_dynamic key is not correct at all... I am just trying to show what I would like to be able to do if possible.

When the resource creates the entries, from the API, if I do it in a range as shown below; if someone needed to change the range for the first pool (in example) to 1-50,52-99, it would delete the entry and re-create it. whereas if they are creating the entry with each entry being created individually it wouldn't delete all of the individual entries, optimally.

I can do the following and it works fine... but being able to add the VLANs individually from a loop would be preferable.

module "vlan_list" {
  depends_on  = [module.vlan_pools]
  source      = "../modules/add_vlans"
  vlan_list = {
    "access" = {
      vlan_pool   = module.vlan_pools.vlan_pool["access"]
      from        = 1
      to          = 99
    },
    "vmm_dynamic" = {
      alloc_mode  = "dynamic"
      vlan_pool   = module.vlan_pools.vlan_pool["vmm_dynamic"]
      from        = 1000
      to          = 1199
    },
    "l3out_1" = {
      vlan_pool   = module.vlan_pools.vlan_pool["l3out"]
      from        = 4
      to          = 4
    },
    "l3out_2" = {
      vlan_pool   = module.vlan_pools.vlan_pool["l3out"]
      from        = 100
      to          = 101
    },
  }
}

Thanks in advance for help on this.

Just as one more point of reference... This is how I had previously accomplished this with Python, but I am trying to move this to native Terraform

def vlan_list_full(vlan_list):
    full_vlan_list = []
    if re.search(r',', str(vlan_list)):
        vlist = vlan_list.split(',')
        for v in vlist:
            if re.fullmatch('^\\d{1,4}\\-\\d{1,4}$', v):
                a,b = v.split('-')
                a = int(a)
                b = int(b)
                vrange = range(a,b+1)
                for vl in vrange:
                    full_vlan_list.append(vl)
            elif re.fullmatch('^\\d{1,4}$', v):
                full_vlan_list.append(v)
    elif re.search('\\-', str(vlan_list)):
        a,b = vlan_list.split('-')
        a = int(a)
        b = int(b)
        vrange = range(a,b+1)
        for v in vrange:
            full_vlan_list.append(v)
    else:
        full_vlan_list.append(vlan_list)
    return full_vlan_list

def vlan_pool
    for z in range(1, 3):
        vgroup = 'VLAN_Grp%s' % (z)
        vgrp = 'VGRP%s_Allocation' % (z)
        templateVars['Allocation_Mode'] = templateVars[vgrp]
        if re.search(r'\d+', str(templateVars[vgroup])):
            vlan_list = vlan_list_full(templateVars[vgroup])
            for v in vlan_list:
                vlan = str(v)
                if re.fullmatch(r'\d+', vlan):
                    templateVars['VLAN_ID'] = int(vlan)

                    # Add VLAN to VLAN Pool File
                    create_tf_file('a+', dest_dir, dest_file, template, **templateVars)

I can't seem to find any examples of how to do this without Python.

like image 694
scotttyso Avatar asked Sep 10 '25 05:09

scotttyso


1 Answers

If you want to create a map with a single key of access and a list of values, then you can use ellipsis operator (...). Also your syntax is incorrect. Thus, the following should be used in this case:

  vlan_list = {
      for i in range(1, 100): 
        "access" => {
          vlan_pool   = module.vlan_pools.vlan_pool["access"]
          from        = i
          to          = i
        }...
   }
like image 60
Marcin Avatar answered Sep 13 '25 03:09

Marcin