Terraform tuple to map conversion

12,657

You have 3 forms, to make the loop.

1º Using dynamic block... Example:

resource "aws_security_group" "ecc_default" {
  count       = length(var.clusters)
  vpc_id      = var.vpc_id
  name        = "${terraform.workspace}.ecc-${var.clusters[count.index].engine}.sg"
  description = "${terraform.workspace}.ecc-${var.clusters[count.index].engine}.sg"

  ingress {
    from_port   = var.clusters[count.index].port
    to_port     = var.clusters[count.index].port
    protocol    = "tcp"
    cidr_blocks = [var.vpc_cidr]
  }

  dynamic "ingress" {
    iterator = item
    for_each = var.enable_vpn ? var.vpn_cidr : []

    content {
      from_port   = var.clusters[count.index].port
      to_port     = var.clusters[count.index].port
      protocol    = "tcp"
      cidr_blocks = [item.value["cidr"]]
      description = item.value["description"]
    }
  }
...
...
}

2º Using for_each... (click here for answer)

3º Using arithmetic iteration... (click here for answer) Example:

resource "aws_security_group_rule" "ecc_internal" {
    count = length(var.ingress_security_groups) > 0 ? length(var.ingress_security_groups) * length(var.clusters) : 0
    security_group_id = aws_security_group.ecc_default[count.index % length(var.clusters)].id
    type = "ingress"
    from_port = var.clusters[count.index % length(var.clusters)].port
    to_port = var.clusters[count.index % length(var.clusters)].port
    protocol = "tcp"
    source_security_group_id = var.ingress_security_groups[floor(count.index / length(var.clusters))]
}

And, for the error on convert: try to use coalescelist with fake replacement. Like this:

resource "aws_lb_listener_certificate" "default" {
  count           = (coalescelist(data.aws_alb_listener.https, [{ certificate_arn = "" }])[0].certificate_arn != var.certificate_arn) ? local.enabled : 0
  listener_arn    = coalescelist(data.aws_alb_listener.https, [{ arn = "" }])[count.index].arn
  certificate_arn = var.certificate_arn
}

The conversion of a tuple ([N1, N2, N3]) to a map ({N1 = "", N2 = ""}), is really not allowed. But, you can use a fake replacement to make the job. tomap needs an object for argument. And map, needs multiple args to make the result. Try to manipulate your list, to convert in map, with another form like this (if you want just convert, and not solve the problem):

environment_vars = jsonencode([
      for key in sort(keys(data.external.environment_vars.result)) : {
        name  = key
        value = lookup(data.external.environment_vars.result, key)
    }])

I recommend for you: Terraform tips & tricks: loops, if-statements, and gotchas

Share:
12,657
Murat
Author by

Murat

Updated on June 04, 2022

Comments

  • Murat
    Murat almost 2 years

    I am pretty new to Terraform and I am using this function to re-use same list of subnets to launch more instances than subnets I have. (It'll just loop over) This works great if I provide my own map of subnets but the data in the remote state is a tuple and I get this error:

    Invalid value for "inputMap" parameter: lookup() requires a map as the first argument. data.terraform_remote_state.vpc.outputs.private_subnets is tuple with 4 elements

    I have also tried the tomap( function but this fails with:

    Invalid value for "v" parameter: cannot convert tuple to map of any single type.

    Here is my code:

      count                  = var.instance_count
      ami                    = var.ami
      instance_type          = "t2.medium"
      subnet_id              = lookup(data.terraform_remote_state.vpc.outputs.private_subnets, count.index%length(data.terraform_remote_state.vpc.outputs.private_subnets))
      vpc_security_group_ids = ["${data.terraform_remote_state.foo_sg.outputs.foo_sg_id}"]
      key_name               = var.key_name
      iam_instance_profile   = var.iam_instance_profile
    
      user_data = <<-EOF
                  #!/bin/bash
                  hostnamectl set-hostname --static "${var.app_name}-${count.index + 1}.${data.terraform_remote_state.vpc.outputs.private_zone_domain_name}"
                  echo "127.0.0.1 localhost.localdomain localhost4 localhost4.localdomain4 ${var.app_name}-${count.index + 1}.${data.terraform_remote_state.vpc.outputs.private_zone_domain_name} localhost" > hosts
                  echo "::1 localhost localhost.localdomain localhost6 localhost6.localdomain6" >> hosts
                  EOF
    
      tags = {
        Name                 = "${var.app_name}-${count.index +1}.${data.terraform_remote_state.vpc.outputs.private_zone_domain_name}"
    }
    
    

    Like I said my goal is to re-use the 4 subnets in that remote state I have, so if I want 6 instances it would loop through the 4 I have and the 5th and 6th instance would be on subnet 1 and 2 in the Tuple. Any suggestions would be appreciated!