Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Terraform - inverting a map

Tags:

terraform

I'm stuck trying to write a terraform expression which can turn this:

subnets = {
   my_subnet_1 = {
     nsg       = "my_nsg_1",
     addresses = "my_addresses_1"
   }
   my_subnet_2 = {
     nsg       = "my_nsg_2",
     addresses = "my_addresses_2"
   }
}

into

nsgs_assocs = {
  my_nsg_1 = "my_subnet_1"
  my_nsg_2 = "my_subnet_2"
}

I've tried the following:

locals {
  nsgs_assocs = zipmap(
    var.subnets.*.nsg,
    keys(var.subnets)
  )
}

but this gives an error:

Error: Invalid function argument

  on ..\..\modules\vnet\main.tf line 22, in locals:
  21:   nsgs_assocs = zipmap(
  22:     var.subnets.*.nsg,
  23:     keys(var.subnets)
  24:   )

Invalid value for "keys" parameter: element 0: string required.

For context, I've inherited a bunch of scripts which I'm trying to refactor without changing the results of a terraform plan.

One of the modules has a lot of related lookup maps - e.g:

nsgs_assocs = {
  my_nsg_1 = "my_subnet_1"
  my_nsg_2 = "my_subnet_2"
}

subnet_addresses = {
  my_subnet_1 = "my_addresses_1"
  my_subnet_2 = "my_addresses_2"
}

which I've condensed into my first above sample which I think will be more maintainable in the long run.

However, for backward compatibility with the existing terraform state I need to generate the original nsgs_assocs inside my module so that a for_each continues to use the nsg name as the resource key instead of the subnet name (which causes a destroy / create pair due to the key change).

like image 493
mclayton Avatar asked Jan 24 '23 14:01

mclayton


2 Answers

You're on the right track. It does not work, because splat expression works with arrays, and var.subnets is a map. In order to fix it, you need to convert it into array and it can be done by using values terraform function:

locals {
  nsgs_assocs = zipmap(
    values(var.subnets)[*].nsg,
    keys(var.subnets)
  )
}
like image 68
rkm Avatar answered Jan 27 '23 04:01

rkm


If you have:

variable "subnets" {
    default = {
       my_subnet_1 = {
         nsg       = "my_nsg_1",
         addresses = "my_addresses_1"
       }
       my_subnet_2 = {
         nsg       = "my_nsg_2",
         addresses = "my_addresses_2"
       }
    }
}

then the following is incorrect

var.subnets.*.nsg

Thus, it should be values(var.subnets).*.nsg:

locals {
  nsgs_assocs = zipmap(
    values(var.subnets).*.nsg,
    keys(var.subnets)
  )
}

resulting in:

{
  "my_nsg_1" = "my_subnet_1"
  "my_nsg_2" = "my_subnet_2"
}
like image 30
Marcin Avatar answered Jan 27 '23 04:01

Marcin