How to set hostname with cloud-init and Terraform?

ilvidel picture ilvidel · Jan 23, 2019 · Viewed 7.6k times · Source

I am starting with Terraform. I am trying to make it set a friendly hostname, instead of the usual ip-10.10.10.10 that AWS uses. However, I haven't found how to do it.

I tried using provisioners, like this:

provisioner "local-exec" {
   command = "sudo hostnamectl set-hostname friendly.example.com"
}

But that doesn't work, the hostname is not changed.

So now, I'm trying this:

resource "aws_instance" "example" {
  ami           = "ami-XXXXXXXX"
  instance_type = "t2.micro"
  tags = {
    Name    = "friendly.example.com"
  }
  user_data = "${data.template_file.user_data.rendered}"
}

data "template_file" "user_data" {
  template = "${file("user-data.conf")}"
  vars {
    hostname = "${aws_instance.example.tags.Name}"
  }
}

And in user-data.conf I have a line to use the variable, like so:

hostname = ${hostname}

But this gives me a cycle dependency:

$ terraform apply
Error: Error asking for user input: 1 error(s) occurred:
* Cycle: aws_instance.example, data.template_file.user_data

Plus, that would mean I have to create a different user_data resource for each instance, which seems a bit like a pain. Can you not reuse them? That should be the purpose of templates, right?

I must be missing something, but I can't find the answer. Thanks.

Answer

Matt Schuchard picture Matt Schuchard · Jan 23, 2019

Using a Terraform provisioner with the local-exec block will execute it on the device from which Terraform is applying: documentation. Note specifically the line:

This invokes a process on the machine running Terraform, not on the resource. See the remote-exec provisioner to run commands on the resource.

Therefore, switching the provisioner from a local-exec to a remote-exec:

provisioner "remote-exec" {
  inline = ["sudo hostnamectl set-hostname friendly.example.com"]
}

should fix your issue with setting the hostname.