amazon-ec2terraformtuplesuser-data

Get IPs as string/list in Terraform


I'm beginning by creating a few machines:

resource "aws_instance" "servers" {
  count = 20
  instance_type = "t2.micro"
  ...........................
}

and then, I want to create a "master machine", like this:

resource "aws_instance" "master" {
  instance_type = "t2.micro"
  .................................
  user_data = <<-EOF
    #!/bin/bash
    yum -y install python-pip
    mkdir /ansible
    cd /ansible
    touch inventory
    echo "${aws_instance.servers[*].public_ip}" > inventory
    echo "server_hostname" >> inventory
    pip install ansible
  EOF
}

Basically I want the IPs of the machines to be in my Ansible inventory on the master machine. My issue however is that when I try to apply the terraform, I run into this:

aws_instance.servers is tuple with 2 elements
ā”‚ Cannot include the given value in a string template: string required.

How can I solve this?

Tried to get a list of the IPs of some EC2 machines in order to have it in a file on a master EC2, via user_data script. However, the issue is that I can't convert it from tuple.


Solution

  • Since you have more than one IP address to include, you will need to explain to Terraform how you want to format those multiple IP addresses into a single string to include into the template result.

    For example, you could tell Terraform to produce a space-separated sequence of IP addresses:

    resource "aws_instance" "master" {
      instance_type = "t2.micro"
      .................................
      user_data = <<-EOF
        #!/bin/bash
        yum -y install python-pip
        mkdir /ansible
        cd /ansible
        touch inventory
        echo "${join(" ", aws_instance.servers[*].public_ip)}" > inventory
        echo "server_hostname" >> inventory
        pip install ansible
      EOF
    }
    

    This example uses the join function to concatenate the IP addresses together with spaces.

    If you need something more specialized and would benefit from writing an entire template block per element then you can alternatively use template directives. For example, if you want to run a separate command for each IP address:

    resource "aws_instance" "master" {
      instance_type = "t2.micro"
      .................................
      user_data = <<-EOF
        #!/bin/bash
        yum -y install python-pip
        mkdir /ansible
        cd /ansible
        touch inventory
        %{ for ip in aws_instance.servers[*].public_ip ~}
        echo "${ip}" >> inventory
        %{ endfor ~}
        echo "server_hostname" >> inventory
        pip install ansible
      EOF
    }
    

    (Note that the above will generate a concatenation >> for even the first IP address, which is different from what you originally shared but I decided to keep it simple since the point of this example is only to show the template for syntax. Adjust as needed!)