Create a map from two lists

Hi,

I’d appreciate any help I can get. I’ve been stuck on this for days and can’t seem to figure it out. My goal is to create an output like this:

Goal:

Outputs:    

ec2_instances = [
      "i-07a8ed7dda10ecc12": 35.155.XXX.XX,
      "i-06e93292096ad880f": 35.155.XXX.XX,
    ]

This is what the current output (local.ec2_comb) looks like:

 ec2_instances = [
  {
"ID" = 0
"IP" = "i-0bcfff94e8d2f9849"
  },
  {
"ID" = 0
"IP" = "i-01fba46588f5ecee7"
  },
  {
"ID" = 1
"IP" = "35.155.XXX.XX"
  },
  {
"ID" = 1
"IP" = "35.155.XXX.XX"
  },

I am using this to create the output:

output "ec2_instances" {
  value = local.ec2_comb
}

locals {
  ec2_ids = flatten(tolist([module.ec2_instances.id, module.ec2_instances_dynamodb.id]))
  ec2_ips = flatten(tolist([module.ec2_instances.public_ip, module.ec2_instances_dynamodb.public_ip]))
  
  pair    = concat([local.ec2_ids, local.ec2_ips])

  ec2_comb = flatten([
    for ID, IPs in local.pair : [
      for IP in IPs : {
        ID = ID
        IP = IP
      }
    ]
  ])
}

Output of locals.ec2_ids:

ec2_instance_id = [
  "i-0eda41e4452281af2",
  "i-018baefc331b68cb3",
]

Output of locals.ec2_ips:

  ec2_instance_public_ips = [
  "35.155.XXX.XX",
  "35.155.XXX.XX",
]

Output of locals.pair :

  ec2_instances = [
  [
    "i-0eda41e4452281af2",
    "i-018baefc331b68cb3",
  ],
  [
  "35.155.XXX.XX",
  "35.155.XXX.XX",
  ],
]

The reason I am using the tolist() function is because the modules create a tuple type value (e.g module.ec2_instances.id ) for the EC2 instance.

So in this case, the instance IDs and public IPs are tuples.

Thank you in advance for the help.

Hi @hadens,

I think there are a few different ways to get this done. My instinct is to flip the problem the other way around and deal with each of the modules separately first and then combine the two together afterwards, like this:

locals {
  main_instance_addrs = tomap({
    for i, id in module.ec2_instances.id :
    id => module.ec2_instances.public_ip[i]
  })
  dynamodb_instance_addrs = tomap({
    for i, id in module.ec2_instances_dynamodb.id :
    id => module.ec2_instances_dynamodb.public_ip[i]
  })
  instance_addrs = merge(
    local.main_instance_addrs,
    local.dynamodb_instance_addrs,
  )
}

I think that gets the result you were looking for here, but please let me know if I misunderstood.

1 Like

Hi @apparentlymart,

Thanks for helping out. Your approach did work! However, it only works when one instance is created per module.

I tested it with two instances for the “main instances” (main_instance_addrs) and one instance for (dynamodb_instance_addr) and obtained these results.

dynamodb_ec2_instance_info = {
  "i-087750beb58cb161b" = "54.149.XX.XX"
}
ec2_instance_info = {
  "i-0249412693e02700a" = "34.221.XX.XX"
  "i-0d17abc6ac2a1a002" = "34.221.XX.XX"
}
ec2_instances = {
  "i-0249412693e02700a" = "34.221.XX.XX"
  "i-0d17abc6ac2a1a002" = "34.221.XX.XX"
} 

It appears that the merged result totally skips over the dynamo_db map. Any ideas?

Thanks again!

Hi @hadens,

I’m not sure what happened there… it’s weird that you’d only see the elements from one of the maps in the result of merge. Would you mind sharing your full implementation that produced this so I can make sure I’m following the chain of operations properly? Thanks.

Hi @apparentlymart,

This is what I am using to create the EC2 instances:

module "ec2_instances" {
  source  = "terraform-aws-modules/ec2-instance/aws"
  version = "2.12.0"

  name           = "s3_rw_access"
  instance_count = 1

  ami           = data.aws_ami.amazon_linux.id
  instance_type = "t2.micro"

  key_name             = aws_key_pair.deployer.key_name
  iam_instance_profile = module.s3_read_write.profile_name

  vpc_security_group_ids = [module.vpc.default_security_group_id, module.nsg.this_security_group_id]
  subnet_id              = module.vpc.public_subnets[0]

  tags = local.common_tags
}

module "ec2_instances_dynamodb" {
  source  = "terraform-aws-modules/ec2-instance/aws"
  version = "2.12.0"

  name           = "dynamodb_rw_access"
  instance_count = 1

  ami           = data.aws_ami.amazon_linux.id
  instance_type = "t2.micro"

  key_name             = aws_key_pair.deployer.key_name
  iam_instance_profile = module.DynamoDB_read_write.profile_name

  vpc_security_group_ids = [module.vpc.default_security_group_id, module.nsg.this_security_group_id]
  subnet_id              = module.vpc.public_subnets[0]

  tags = local.common_tags
} 

merge only displays one map when instance_count = X is greater than 1.

Please let me know if you need anything else. Thanks!

Hi @hadens,

I’d also like to see the locals block you’re using. The result you shared in your earlier message suggested that you’d modified it a little from what I suggested (the names were different) so I want to make sure I understand how the new names you are using relate to one another and to my earlier suggestion. Thanks!

Hi @apparentlymart,

Sorry I misunderstood. Here is my locals block:

locals {
  # ec2_ids = flatten(tolist([module.ec2_instances.id, module.ec2_instances_dynamodb.id]))
  # ec2_ips = flatten(tolist([module.ec2_instances.public_ip, module.ec2_instances_dynamodb.public_ip]))

  # pair = concat([local.ec2_ids, local.ec2_ips])

  # ec2_comb = flatten([
  #   for ID, IPs in local.pair : [
  #     for IP in IPs : {
  #       ID = ID
  #       IP = IP
  #     }
  #   ]
  # ])

  main_instance_addrs = tomap({
    for i, id in module.ec2_instances.id :
    id => module.ec2_instances.public_ip[i]
  })
  dynamodb_instance_addrs = tomap({
    for i, id in module.ec2_instances_dynamodb.id :
    id => module.ec2_instances_dynamodb.public_ip[i]
  })
  instance_addrs = merge(
    local.main_instance_addrs,
    local.dynamodb_instance_addrs,
  )
}

And the output I’m using:

 output "ec2_instances" {
   value = local.main_instance_addrs
}

Thanks!

Hi @hadens,

It looks like your output is referring to local.main_instance_addrs, rather than to the merged result local.instance_addrs. Hopefully this will get the result you were looking for:

output "ec2_instances" {
  value = local.instance_addrs
}
1 Like

@apparentlymart,

Boy do I feel dumb… that was definitely the issue. The output is now displaying the merged maps correctly.

Thank you for all your help! :+1:

No problem! Easy to get the names mixed up when they all look so similar. I’m glad it worked out!