CDuv
CDuv

Reputation: 2260

Merge some keys of a Terraform map

I am trying to "merge" a list of objects (each items of local.what_i_have below) together by keeping some keys as scalar (identical values, here instance_id and tag_name) and merging others (here tag_value) into a set/list.

locals {
  what_i_have_v2 = {
    "cluster=foo|instance=foo#1|tag=tag1" = [
      {
        instance_id = "i-fff11111"
        tag_name = "tag1"
        tag_value = "val1_a"
      },
      {
        instance_id = "i-fff11111"
        tag_name = "tag1"
        tag_value = "val1_b"
      },
    ],
    "cluster=foo|instance=foo#2|tag=tag1" = [
      {
        instance_id = "i-fff22222"
        tag_name = "tag1"
        tag_value = "val1_a"
      },
      {
        instance_id = "i-fff22222"
        tag_name = "tag1"
        tag_value = "val1_b"
      },
    ],
    "cluster=bar|instance=bar#1|tag=tag1" = [
      {
        instance_id = "i-bbb11111"
        tag_name = "tag1"
        tag_value = "val1_a"
      },
    ],
    "cluster=bar|instance=bar#1|tag=tag2" = [
      {
        instance_id = "i-bbb11111"
        tag_name = "tag2"
        tag_value = "val2_a"
      },
      {
        instance_id = "i-bbb11111"
        tag_name = "tag2"
        tag_value = "val2_b"
      },
    ],
  },
}

Any of the following output might work for me:

locals {
  what_i_want_v1 = {
    "cluster=foo|instance=foo#1" = {
      instance_id = "i-fff11111"
      tag_name = "tag1"
      tag_value = ["val1_a", "val1_b"] # Values for identical `what_i_have` key are now "merged" in a set/list
    },
    "cluster=foo|instance=foo#2" = {
      instance_id = "i-fff22222"
      tag_name = "tag1"
      tag_value = ["val1_a", "val1_b"]
    },
    "cluster=bar|instance=bar#1" = {
      instance_id = "i-bbb11111"
      tag_name = "tag1"
      tag_value = ["val1_a"]
    },
  },

  what_i_want_v2 = [ # Not a map anymore but I don't mind
    {
      instance_id = "i-fff11111"
      tag_name = "tag1"
      tag_value = ["val1_a", "val1_b"]
    },
    {
      instance_id = "i-fff22222"
      tag_name = "tag1"
      tag_value = ["val1_a", "val1_b"]
    },
    {
      instance_id = "i-bbb11111"
      tag_name = "tag1"
      tag_value = ["val1_a"]
    },
  ],

  what_i_want_v3 = [
    {
      instance_id = "i-fff11111"
      tag_name = "tag1"
      tag_value = "val1_a+val1_b" # Not an set/list but a string with a "+" separator
    },
    {
      instance_id = "i-fff22222"
      tag_name = "tag1"
      tag_value = "val1_a+val1_b"
    },
    {
      instance_id = "i-bbb11111"
      tag_name = "tag1"
      tag_value = "val1_a"
    },
  ],
}

Is this possible?

I naively tried to merge() to see what it does (local.attempt_1 below):

locals {
 attempt_1 = {for k, v in local.what_i_have: k => merge(v...)}
}

But it (obviously) swallows values:

# attempt_1 =
{
  "cluster=bar|instance=bar#1" = {
    instance_id = "i-bbb11111"
    tag_name    = "tag1"
    tag_value   = "val1_a"
  }
  "cluster=foo|instance=foo#1" = {
    instance_id = "i-fff11111"
    tag_name    = "tag1"
    tag_value   = "val1_b"
  }
  "cluster=foo|instance=foo#2" = {
    instance_id = "i-fff22222"
    tag_name    = "tag1"
    tag_value   = "val1_b"
  }
}

Context: I am building what_i_have as a for_each for a aws_ec2_tag resource, to tag instances from a list of tags to have.

Update 1: what_i_have can contain any tag name, updated the map in question (as what_i_have_v2).

Here is the original flawed what_i_have:

locals {
  what_i_have = {
    "cluster=foo|instance=foo#1" = [
      {
        instance_id = "i-fff11111"
        tag_name = "tag1"
        tag_value = "val1_a"
      },
      {
        instance_id = "i-fff11111"
        tag_name = "tag1"
        tag_value = "val1_b"
      },
    ],
    "cluster=foo|instance=foo#2" = [
      {
        instance_id = "i-fff22222"
        tag_name = "tag1"
        tag_value = "val1_a"
      },
      {
        instance_id = "i-fff22222"
        tag_name = "tag1"
        tag_value = "val1_b"
      },
    ],
    "cluster=bar|instance=bar#1" = [
      {
        instance_id = "i-bbb11111"
        tag_name = "tag1"
        tag_value = "val1_a"
      },
    ],
  },
}

Upvotes: 1

Views: 48

Answers (1)

Paolo
Paolo

Reputation: 26220

You could flatten the values and then iterate over them:

  flattened = flatten(values(local.what_i_have))

  what_i_want_v2 = [
    for instance in distinct([for item in local.flattened : item.instance_id]) : {
      instance_id = instance
      tag_name    = "tag1"
      tag_value   = distinct([for item in local.flattened : item.tag_value if item.instance_id == instance])
    }
  ]
> local.what_i_want_v2
[
  {
    "instance_id" = "i-bbb11111"
    "tag_name" = "tag1"
    "tag_value" = tolist([
      "val1_a",
    ])
  },
  {
    "instance_id" = "i-fff11111"
    "tag_name" = "tag1"
    "tag_value" = tolist([
      "val1_a",
      "val1_b",
    ])
  },
  {
    "instance_id" = "i-fff22222"
    "tag_name" = "tag1"
    "tag_value" = tolist([
      "val1_a",
      "val1_b",
    ])
  },
]

Upvotes: 0

Related Questions