Terraform: Unclear how to add user_data to an aws_instance

Created on 3 Aug 2014  ·  23Comments  ·  Source: hashicorp/terraform

I have been trying to add user_data to my aws instance. Things I tried before getting some success....

  • Adding the entire string over multiple lines (which terraform complained that it found an unexpected newline char)....
    user_data = "line one
    line two
    line three"
  • Sending the lines as an array (which is a format I have seen in other parts of the config, i.e. shell script provisioning)
    user_data = [
    "line one",
    "line two",
    "line three"
    ]
  • The base64 representation of the user_data (whcih is what the amazon api expects)....
    user_data = "bGluZSBvbmUKbGluZSB0d28KbGluZSB0aHJlZQo="
  • Finally, escaping the newline (which worked)
    user_data = "line onenline twonline threen"

I think this needs to be clearer in the documentation, or providing other ways of providing the information (if there isn't already, i.e., can we do multi-line strings within the syntax of the config file?) There doesn't seem to be any mention of multi-line string values on the syntax page to give any hints either.

bug

Most helpful comment

@vertis Thank You

This can essentially be closed as the following works:

Create a multiline user_data file (example.txt):

echo "hello"
echo "world"

Then in your example.tf

user_data = ${file("example.tf")}

All 23 comments

I wonder if you can provide a full path, that would be pretty helpful if using #cloud-init for example

I would personally prefer HERDOC style, multi-line string, rather than a path to a file. That way I can just create a variable with my cloud-init as a default and include it in whatever instances I would like.

What would be cool is some kind of import syntax which would allow a file to be imported into a variable, which would keep the abstraction for the user_data (it is just a string after all), but the also allow any string in the system to be read from a file (which could be good if I run a packer command, i could output the image id to a file in my terraform directory for example).

@mattsoftware Looks like the reason you cant provide a base64 encoded string yourself is because on line 339 (https://github.com/mitchellh/goamz/blob/2e353d54f19f99fa67b93cddc3b9d86ede74f566/ec2/ec2.go#L339) the userdata gets base64 encoded. Which would mean your string user_data = "bGluZSBvbmUKbGluZSB0d28KbGluZSB0aHJlZQo=" would actually translate to user_data = "YkdsdVpTQnZibVVLYkdsdVpTQjBkMjhLYkdsdVpTQjBhSEpsWlFvPQo=" before it gets passed to the Amazon API

This is definitely failing at the configuration load phase, Not the aws instance creation phase.

I was sure that it was possible to read in a file in terraform, but now I can't find the section.

perfect. Perhaps that could be a great example for the aws_instance page :)

@vertis Thank You

This can essentially be closed as the following works:

Create a multiline user_data file (example.txt):

echo "hello"
echo "world"

Then in your example.tf

user_data = ${file("example.tf")}

When I run the following:

resource "aws_instance" "docker_host" {
  instance_type = "t1.micro"
  ami = "${lookup(var.aws_amis, var.aws_region)}"
  count = 2
  key_name = "${var.aws_key_name}"
  security_groups = "coreos-testing"
  user_data = ${file("cloud-config.yaml")}
}

The resulting user data in the created instance is:

${file("cloud-config.yaml")}

Am I missing something?

What is the output of terraform apply?

terraform@terraform-demo:/home/terraform/demo$ terraform apply -var-file=terraform.tfvars 
aws_instance.docker_host.1: Creating...
  ami:             "" => "ami-04a2766c"
  instance_type:   "" => "t1.micro"
  key_name:        "" => "monkey"
  security_groups: "" => "coreos-testing"
  user_data:       "" => "7a84015f3404a5d4bba3c197fc67b633758cc49a"
aws_instance.docker_host.0: Creating...
  ami:             "" => "ami-04a2766c"
  instance_type:   "" => "t1.micro"
  key_name:        "" => "monkey"
  security_groups: "" => "coreos-testing"
  user_data:       "" => "7a84015f3404a5d4bba3c197fc67b633758cc49a"
aws_instance.docker_host.1: Creation complete
aws_instance.docker_host.0: Creation complete

Apply complete! Resources: 2 added, 0 changed, 0 destroyed.

The state of your infrastructure has been saved to the path
below. This state is required to modify and destroy your
infrastructure, so keep it safe. To inspect the complete state
use the `terraform show` command.

State path: terraform.tfstate

Outputs:

  addresses = Public DNS Addresses: ec2-54-210-249-139.compute-1.amazonaws.com,ec2-54-210-249-115.compute-1.amazonaws.com

Im getting the same thing.

Seems the Interpolation isnt behaving correctly

I tried both

user_data = ${file("test.txt")}

Which yielded

ubuntu@ip-10-71-135-46:~$ curl http://169.254.169.254/latest/user-data
${file("test.txt")}

and

 user_data = file("test.txt")

which yielded

ubuntu@ip-10-178-169-89:~$ curl http://169.254.169.254/latest/user-data
file("test.txt")

seems user_data is being taken as a String Literal and not getting Interpolated

This looks like a bug, tagged, thanks!

Fixed, thanks for finding this.

I'm having this issue on 0.1.1, which should include this hash.

I have a lunch configuration with user_data = ${file("CDR-WS 1.3.0 (1.7.0-build-103-31d5d67).ps1")}

Build succeeds with

aws_launch_configuration.as_conf: Creating...
  image_id:          "" => "..."
  instance_type:     "" => "..."
  key_name:          "" => "..."
  name:              "" => "CDR-WS 1.3.0 (1.7.0-build-103-31d5d67)"
  security_groups.#: "" => "2"
  security_groups.0: "" => "sg-621b8807"
  security_groups.1: "" => "sg-e814878d"
  user_data:         "" => "${file(\"CDR-WS 1.3.0 (1.7.0-build-103-31d5d67).ps1\")}"
aws_launch_configuration.as_conf: Creation complete

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

The userdata in the LC is ${file("CDR-WS 1.3.0 (1.7.0-build-103-31d5d67).ps1")}

Same effect with:

  • user_data = ${file('CDR-WS 1.3.0 (1.7.0-build-103-31d5d67).ps1')}
  • user_data = "${file(\"CDR-WS 1.3.0 (1.7.0-build-103-31d5d67).ps1\")}"
  • user_data = "${file('CDR-WS 1.3.0 (1.7.0-build-103-31d5d67).ps1')}"

I know this is closed, but this was not working for me either. I "fixed" this by backing out commit ba68be5672814d0f0ad502164183b28bc638c2ba I don't know what that even does, but it was causing the user data to not be processed correctly at all. Without it, it works.

Failed to load root config module: Error parsing /root/Terraform/test.tf: At 25:15: illegal char

Line 25:
user_data = ${file("user_data.tf")}

@naul11 user_data = "${file(...

I must leave my 2cents here:

  user_data     = "${file("user-data.yml")}"

This worked for me, though I could have sworn I didn't need to do this before:

user_data            = "${base64encode(file("${path.module}/user-data.yaml"))}"

How can you pass to a user_data's script some arguments? Something like:

user_data = "${file("user-data.ps1") -Arg1 ${var.variable}}"

@Daniel96 : take a look at the template resource

I'm going to lock this issue because it has been closed for _30 days_ ⏳. This helps our maintainers find and focus on the active issues.

If you have found a problem that seems similar to this, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further.

Was this page helpful?
0 / 5 - 0 ratings