Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to build up terraform local_file dynamically

I want to create few EC2 instances and grab their private_ips into a ec2.ini style file for further use with Ansible.

resource "local_file" "ec2_id" {
  count = var.instance_count
  content  = "${aws_instance.instance[count.index].private_ip} ansible_ssh_user=ec2-user\n"
  filename = "ec2.ini"
}

This always prints the private_ip of latest EC2 instance created.

Any idea how to solve this.

Update:-

data "template_file" "hehe" {
  count = var.instance_count
  template = "${element(aws_instance.instance.*.private_ip, count.index)} ansible_ssh_user=ec2-user subnetmask=${element(split("/", data.aws_subnet.selected-subnet-id.cidr_block),1)}\n"
}


resource "local_file" "ec2_id" {
  count = var.instance_count
  content  = "${element(data.template_file.hehe.*.rendered, count.index)}"
  filename = "ec2.ini"
}

does not work. gives me the last created instance private_ip.

like image 214
Biplab Avatar asked Oct 20 '25 15:10

Biplab


1 Answers

When you use count inside a resource you are asking Terraform to create multiple instances of that resource. However, in your case you didn't include count.index in the filename argument and so all of your instances are competing to overwrite the same filename ec2.ini, and so only one of them can "win".

It sounds like your goal is to create only one file that contains all of the IP addresses. This is very close to one of the examples that are in the Terraform String Templates documentation at the time I write this, which we can adapt to your goal like this:

resource "local_file" "ec2_iini" {
  filename = "ec2.ini"
  content = <<-EOT
    %{ for ip in aws_instance.instance.*.private_ip ~}
    ${ip} ansible_ssh_user=ec2-user
    %{ endfor ~}
  EOT
}

In the above example the local_file resource itself does not have count set, because our goal is to create only one file. Instead, we use Terraform's template for directive to repeat a string template once per instance, gathering the result as a single string which local_file can then use as its content argument.

I used the "heredoc" style of string literal here because I think it makes the for directive easier to read by splitting it over multiple lines. The - in <<-EOT causes Terraform to look at all of the lines between the opening <<-EOT and the closing EOT and find the smallest number of leading spaces those lines have in common, which it will then strip off when rendering. That means that you can have the template indented in your configuration but avoid those indentations appearing in the rendered string, which should look something like this:

10.1.0.34 ansible_ssh_user=ec2-user
10.1.0.5 ansible_ssh_user=ec2-user
10.1.0.92 ansible_ssh_user=ec2-user

The ~ markers on the end of the two %{ ... } directives instruct Terraform's template engine to ignore the newline and whitespace after them, so we can wrap the template over multiple lines without introducing additional newlines into the result. The only line of the template that generates a trailing newline here is the middle line containing the IP address interpolation and the ansible_ssh_user portion, so the result ends up having only one newline per entry as seems to be intended here.

like image 200
Martin Atkins Avatar answered Oct 23 '25 07:10

Martin Atkins



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!