skip to Main Content

I have created a variable of type list(object) on a module directory. My goal is to call this variable under a different directory using a for_each loop. (Tree attached below)

.
├── main.tf
├── output.tf
└── proxmox-instance
    ├── main.tf
    ├── output.tf
    ├── providers.tf
    └── variables.tf

The variable.tf file looks like this

variable "forwarded_ports" {
  description = "Your Template name - Will be cloned from this template"
  type = list(object({
    host_port  = number
    guest_port = number
  }))
  default = [{
    "host_port"  = 2222,
    "guest_port" = 22
  }]
}

While the ./proxmox-instance/main.tf looks like this

resource "iptables_nat" "dnat-front" {
  # Creates an entry for each template specified
  for_each = [for k in var.forwarded_ports : k]

  name           = "iptable-${var.vm_name}-${each.value.host_port}-to-${each.value.guest_port}"
  on_cidr_blocks = ["195.201.172.34"] # Proxmox network device IP here

  dnat {
    iface    = "enp7s0"
    protocol = "tcp"
    to_port  = each.value.host_port                                                  # Custom Port for ingress connections
    nat_ip   = "${proxmox_vm_qemu.vm.default_ipv4_address}:${each.value.guest_port}" # VM Internal IP here
  }
}

The issue occurs while trying to refer the code at the forwarded_ports section below

module "instance" {
  source = "./proxmox-instance"

  vm_name       = "Terraform"
  template_name = "ubuntu-20.04"

  # cloud-init settings
  ipconfig0           = "ip=dhcp"
  cloud_init_user     = "ubuntu"
  cloud_init_password = "password"

  forwarded_ports = [
    {
      host_port  = 2222
      guest_port = 22
    },
    {
      host_port  = 5555
      guest_port = 22
    },
  ]
}

I’ve tried playing with the syntax abit and got different results

Output 1/2

for_each = [for k in var.forwarded_ports : k]


│ Error: Invalid for_each argument
│ 
│   on proxmox-instancemain.tf line 37, in resource "iptables_nat" "dnat-front":
│   37:   for_each = [for k in var.forwarded_ports : k]
│     ├────────────────
│     │ var.forwarded_ports is list of object with 2 elements
│ 
│ The given "for_each" argument value is unsuitable: the "for_each" argument must be a map, or set of strings, and you have provided a value of type tuple.

Output 2/2

for_each = toset([for k in var.forwarded_ports : k])


│ Error: Invalid for_each set argument
│ 
│   on proxmox-instancemain.tf line 37, in resource "iptables_nat" "dnat-front":
│   37:   for_each = toset([for k in var.forwarded_ports : k])
│     ├────────────────
│     │ var.forwarded_ports is list of object with 2 elements
│
│ The given "for_each" argument value is unsuitable: "for_each" supports maps and sets of strings, but you have provided a set containing type object.

2

Answers


  1. You are providing a variable of type list(objects) and you are trying to use a list with for_each (which error output shows you it is not possible) while it can work only with maps or sets. What you could try is the following:

    resource "iptables_nat" "dnat-front" {
      # Creates an entry for each template specified
      for_each = { for i in var.forwarded_ports : i.host_port => {
        host_port  = i.host_port
        guest_port = i.guest_port
      } }
    
      name           = "iptable-${var.vm_name}-${each.value.host_port}-to-${each.value.guest_port}"
      on_cidr_blocks = ["195.201.172.34"] # Proxmox network device IP here
    
      dnat {
        iface    = "enp7s0"
        protocol = "tcp"
        to_port  = each.value.host_port                                                  # Custom Port for ingress connections
        nat_ip   = "${proxmox_vm_qemu.vm.default_ipv4_address}:${each.value.guest_port}" # VM Internal IP here
      }
    }
    

    Alternatively, you could define a locals block and create the map you want there:

    locals {
      fowarded_ports = { for i in local.forwarded_ports : i.host_port => {
        host_port  = i.host_port
        guest_port = i.guest_port
      } }
    }
    

    And then in the resource block:

    resource "iptables_nat" "dnat-front" {
      # Creates an entry for each template specified
      for_each = local.forwarded_ports
    
      name           = "iptable-${var.vm_name}-${each.value.host_port}-to-${each.value.guest_port}"
      on_cidr_blocks = ["195.201.172.34"] # Proxmox network device IP here
    
      dnat {
        iface    = "enp7s0"
        protocol = "tcp"
        to_port  = each.value.host_port                                                  # Custom Port for ingress connections
        nat_ip   = "${proxmox_vm_qemu.vm.default_ipv4_address}:${each.value.guest_port}" # VM Internal IP here
      }
    }
    
    Login or Signup to reply.
  2. forwarded_ports is a list. for_each works with maps. You can easly convert list to map as follows:

     for_each = { for i,v in var.forwarded_ports : i => v}
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search