Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using function templatefile(path, vars) with a remote-exec provisioner

Tags:

terraform

With terraform 0.12, there is a templatefile function but I haven't figured out the syntax for passing it a non-trivial map as the second argument and using the result to be executed remotely as the newly created instance's provisioning step.

Here's the gist of what I'm trying to do, although it doesn't parse properly because one can't just create a local variable within the resource block named scriptstr.

While I'm really trying to get the output of the templatefile call to be executed on the remote side, once the provisioner can ssh to the machine, I've so far gone down the path of trying to get the templatefile call output written to a local file via the local-exec provisioner. Probably easy, I just haven't found the documentation or examples to understand the syntax necessary. TIA

resource "aws_instance" "server" {
  count = "${var.servers}"

  ami           = "${local.ami}"
  instance_type = "${var.instance_type}"
  key_name      = "${local.key_name}"

  subnet_id              = "${element(aws_subnet.consul.*.id, count.index)}"
  iam_instance_profile   = "${aws_iam_instance_profile.consul-join.name}"
  vpc_security_group_ids = ["${aws_security_group.consul.id}"]

  ebs_block_device {
    device_name = "/dev/sda1"
    volume_size = 2
  }

  tags = "${map(
    "Name", "${var.namespace}-server-${count.index}",
    var.consul_join_tag_key, var.consul_join_tag_value
  )}"

  scriptstr = templatefile("${path.module}/templates/consul.sh.tpl",
      {
        consul_version = "${local.consul_version}"

        config = <<EOF
         "bootstrap_expect": ${var.servers},
         "node_name": "${var.namespace}-server-${count.index}",
         "retry_join": ["provider=aws tag_key=${var.consul_join_tag_key} tag_value=${var.consul_join_tag_value}"],
         "server": true
        EOF
      })

  provisioner "local-exec" {
    command = "echo ${scriptstr} > ${var.namespace}-server-${count.index}.init.sh"
  }

  provisioner "remote-exec" {
    script = "${var.namespace}-server-${count.index}.init.sh"

    connection {
      type     = "ssh"
      user     = "clear"
      private_key = file("${local.private_key_file}")
    }
  }
}
like image 432
WeakPointer Avatar asked Jan 25 '23 23:01

WeakPointer


1 Answers

In your question I can see that the higher-level problem you seem to be trying to solve here is creating a pool of HashiCorp Consul servers and then, once they are all booted up, to tell them about each other so that they can form a cluster.

Provisioners are essentially a "last resort" in Terraform, provided out of pragmatism because sometimes logging in to a host and running commands on it is the only way to get a job done. An alternative available in this case is to instead pass the information from Terraform to the server via the aws_instance user_data argument, which will then allow the servers to boot up and form a cluster immediately, rather than being delayed until Terraform is able to connect via SSH.

Either way, I'd generally prefer to have the main body of the script I intend to run already included in the AMI so that Terraform can just run it with some arguments, since that then reduces the problem to just templating the invocation of that script rather than the whole script:

  provisioner "remote-exec" {
    inline = ["/usr/local/bin/init-consul --expect='${var.servers}' etc, etc"]

    connection {
      type     = "ssh"
      user     = "clear"
      private_key = file("${local.private_key_file}")
    }
  }

However, if templating an entire script is what you want or need to do, I'd upload it first using the file provisioner and then run it, like this:

  provisioner "file" {
    destination = "/tmp/consul.sh"
    content = templatefile("${path.module}/templates/consul.sh.tpl", {
        consul_version = "${local.consul_version}"

        config = <<EOF
         "bootstrap_expect": ${var.servers},
         "node_name": "${var.namespace}-server-${count.index}",
         "retry_join": ["provider=aws tag_key=${var.consul_join_tag_key} tag_value=${var.consul_join_tag_value}"],
         "server": true
        EOF
    })
  }

  provisioner "remote-exec" {
    inline = ["sh /tmp/consul.sh"]
  }
like image 124
Martin Atkins Avatar answered May 28 '23 02:05

Martin Atkins