Search code examples
terraformterraform-provider-aws

Obtain value from a local list


I am trying to tag the docker swarm instances using terraform I defined variable and locals as variables.tf

variable "instance_count" {
  default = "3"
}
variable "instance_type" {
  default = "t2.micro"
}
variable "aws_region" {
  default = "us-east-1"
}
variable "ami" {
  default = "ami-09e67e426f25ce0d7"
}
variable "host_name" {
  type    = map(number)
  default = {
    "Manager" = 1
    "Worker" = 2
  }
}

When i refer to this list's each value to assign it as a tag to ec2 instance like this ec2instance.tf

resource "aws_instance" "swarm_instance" {
  count           = var.instance_count
  ami             = var.ami
  instance_type   = var.instance_type
  key_name        = aws_key_pair.dockerswarm.key_name

  tags = {
    Name = "Swarm_Instance-${count.index + 1}"
  }
   tags = {
    Name = "${local.expanded_names}"
  }
locals {
  expanded_names = {
    for name, count in var.host_name : name => [
      for i in range(count) : format("%s-%02d", name, i+1)
    ]
  }
}

Terraform complains local.expanded_names is object with 2 attributes I tried with ${local.expanded_names.value}, but then it complained object does not have an attribute named "value". So how to retrieve the value from the list when value attribute is not available in terraform.


Solution

  • The tags should be strings, in your case I would use jsonencode to get a string out of that object you are building, see my sample code below

    variable "host_name" {
      type    = map(number)
      default = {
        "Manager" = 1
        "Worker" = 2
      }
    }
    
    locals {
      expanded_names = jsonencode({
        for name, count in var.host_name : name => [
          for i in range(count) : format("%s-%02d", name, i+1)
        ]
      })
    }
    
    provider "aws" {
      region = "us-east-1"
    }
    
    resource "aws_instance" "instance" {
      ami           = "ami-1c761163"
      instance_type = "r5.large"
    
      tags = {
        Terraformed = "true"
        Name        = local.expanded_names
      }
    }
    

    if we run a terraform plan on that, here is what we get:

    Terraform will perform the following actions:
    
      # aws_instance.instance will be created
      + resource "aws_instance" "instance" {
          + ami                                  = "ami-1c761163"
          ...
          + instance_state                       = (known after apply)
          + instance_type                        = "r5.large"
          ...
          + subnet_id                            = (known after apply)
          + tags                                 = {
              + "Name"        = jsonencode(
                    {
                      + Manager = [
                          + "Manager-01",
                        ]
                      + Worker  = [
                          + "Worker-01",
                          + "Worker-02",
                        ]
                    }
                )
              + "Terraformed" = "true"
            }
    



    Or maybe what you meant to do is create an array of names:

    • Manager-01
    • Worker-01
    • Worker-02

    Then use that as the instance names... if that is the case your expanded_names should not be an object {} but an array [], then we use that instead of your count, see code sample below:

    variable "host_name" {
      type    = map(number)
      default = {
        "Manager" = 1
        "Worker" = 2
      }
    }
    
    locals {
      expanded_names = flatten([
        for name, count in var.host_name : [
          for i in range(count) : format("%s-%02d", name, i+1)
        ]
      ])
    }
    
    provider "aws" {
      region = "us-east-1"
    }
    
    resource "aws_instance" "instance" {
      for_each = toset(local.expanded_names)
    
      ami           = "ami-1c761163"
      instance_type = "r5.large"
    
      tags = {
        Terraformed = "true"
        Name        = each.value
      }
    }
    

    and a terraform plan on that outputs:

    Terraform will perform the following actions:
    
      # aws_instance.instance["Manager-01"] will be created
      + resource "aws_instance" "instance" {
          + ami                                  = "ami-1c761163"
          ...
          + tags                                 = {
              + "Name"        = "Manager-01"
              + "Terraformed" = "true"
            }
          ...
        }
    
      # aws_instance.instance["Worker-01"] will be created
      + resource "aws_instance" "instance" {
          + ami                                  = "ami-1c761163"
          ...
          + tags                                 = {
              + "Name"        = "Worker-01"
              + "Terraformed" = "true"
            }
          ...
        }
    
      # aws_instance.instance["Worker-02"] will be created
      + resource "aws_instance" "instance" {
          + ami                                  = "ami-1c761163"
          ...
          + tags                                 = {
              + "Name"        = "Worker-02"
              + "Terraformed" = "true"
            }
          ...
        }
    
    Plan: 3 to add, 0 to change, 0 to destroy.