Priya Namasivayam
Priya Namasivayam

Reputation: 11

How to access list of lists which has a map in it using Terraform 0.11?

I have a few EKS node groups. I am trying to access the autoscaling_groups name of all the node groups (eg name = eks-e214c586716a). It's much easier in 0.12, but we are still using 0.11.

[
  [
    {
      "autoscaling_groups" = [
        {
          "name" = "eks-e214c586716a"
        },
      ]
      "remote_access_security_group_id" = "sg-name1"
    },
  ],
  [
    {
      "autoscaling_groups" = [
        {
          "name" = "eks-c866f3f2edb5"
        },
      ]
      "remote_access_security_group_id" = "sg-name2"
    },
  ],
]

This works: aws_eks_node_group.node-group.resources.0.autoscaling_groups.0.name

However, when I iterate through, I am unsuccessful.

count = "${length(var.nodegroups)}"
autoscaling_group_name    = "${element(aws_eks_node_group.node-group.resources.*.autoscaling_groups.*.name, count.index)}"

Upvotes: 1

Views: 3955

Answers (1)

ydaetskcoR
ydaetskcoR

Reputation: 56877

It looks like you're misreading the complex data structure you have there.

You have a list of node groups which in turn contain another list that always has one element that is an object with the keys autoscaling_groups and remote_access_security_group_id. The autoscaling_groups key is then also another list with one element that contains an object with the key name.

Your attempt at doing this was to use:

"${element(aws_eks_node_group.node-group.resources.*.autoscaling_groups.*.name, count.index)}"

Which is saying to loop over the outermost list and then attempt to get the autoscaling_groups key from an object there when at that point it's a list with 1 element in it. You then also tried to loop over the inner most list which only has one element in it so you'd over index that if you went with:

"${element(aws_eks_node_group.node-group.resources.*.*.autoscaling_groups.*.name, count.index)}"

Because element allows wrap around you wouldn't get an out of bound index here when trying to access the second element of a 1 element list, however Terraform doesn't let you use nested splat expressions:

Error: Nested splat expression not allowed

  on main.tf line 33, in resource "local_file" "asg_names":
  33:   content  = "${element(local.eks_node_group.*.*.autoscaling_groups.*.name, count.index)}"

A splat expression (*) cannot be used inside another attribute-only splat
expression.

So to get the autoscaling group names you want to loop over the outermost list and then take the first element of that list, get the autoscaling_groups key, take the first element of that list and then finally get the value from the name key.

Here's a basic worked example of accessing this data structure, using locals for the input and a local_file resource as an output to allow us to loop over it with count:

locals {
  eks_node_group = [
    [
      {
        "autoscaling_groups" = [
          {
            "name" = "eks-e214c586716a"
          },
        ]
        "remote_access_security_group_id" = "sg-name1"
      },
    ],
    [
      {
        "autoscaling_groups" = [
          {
            "name" = "eks-c866f3f2edb5"
          },
        ]
        "remote_access_security_group_id" = "sg-name2"
      },
    ],
  ]
}

resource "local_file" "asg_names" {
  count    = "${length(local.eks_node_group)}"
  filename = "${count.index}.output"
  content  = "${element(local.eks_node_group.*.0.autoscaling_groups.0.name, count.index)}"
}

Running the plan shows the following expected output:

  # local_file.asg_names[0] will be created
  + resource "local_file" "asg_names" {
      + content              = "eks-e214c586716a"
      + directory_permission = "0777"
      + file_permission      = "0777"
      + filename             = "0.output"
      + id                   = (known after apply)
    }

  # local_file.asg_names[1] will be created
  + resource "local_file" "asg_names" {
      + content              = "eks-c866f3f2edb5"
      + directory_permission = "0777"
      + file_permission      = "0777"
      + filename             = "1.output"
      + id                   = (known after apply)
    }

Note that unless you want the wrap around functionality of element as mentioned above you should really be using the more straightforward indexing syntax as list[index]:

resource "local_file" "asg_names" {
  count    = "${length(local.eks_node_group)}"
  filename = "${count.index}.output"
  content  = "${local.eks_node_group[count.index].0.autoscaling_groups.0.name}"
}

Upvotes: 2

Related Questions