Deploy local files to instances without using Terr

2020-03-30 08:22发布

问题:

As several other users who have posted to StackOverflow, I ran into problems with file provisioners, and the Terraform documentation says we should not rely on them.

What's the best way to work around file provisioners - specifically for local config files and scripts?

回答1:

One solution, which works very well and does not require a direct connection to the instance, is to use the userdata as a hook to "install" the files from the base64 version of the file(s).

We can actually embed the files as base64 strings in the userdata initialization scripts. This works for both Windows and Linux instances in AWS, and is compatible also with having a userdata script run on startup.

Solution Description:

  1. During terraform plan, encode whatever local files you need as base64 strings using terraform functions base64encode(file("path/to/file")).
  2. (Optional) Save a marker file (_INIT_STARTED_) at the start of userdata execution; this file will have the creation timestamp of when the userdata execution started.
  3. Before running the actual userdata script, write the base64 strings to text files. (The actual command varies between windows and linux, see examples below.)
  4. Run the userdata script itself (userdata_win.bat or userdata_lin.sh)
  5. (Optional) Finally, save a second marker file (_INIT_COMPLETE_) which will have the creation timestamp of when the userdata script completed. (The absence of this file is also helpful to detect script failures and/or still-running scripts after logging into the instance.)

For AWS Linux instances:

data "template_file" "userdata_lin" {
  template = <<EOF
#!/bin/bash
mkdir -p /home/ubuntu/setup-scripts
cd /home/ubuntu/setup-scripts
touch _INIT_STARTED_
echo ${base64encode(file("${path.module}/userdata_lin.sh"))} | base64 --decode > userdata.sh
echo ${base64encode(file("${path.module}/config.json"))} | base64 --decode > config.json
${file("${path.module}/userdata_lin.sh")}
sudo chmod 777 *
touch _INIT_COMPLETE_
EOF
}

# ...

resource "aws_instance" "my_linux_instance" {
  # ...
  user_data = data.template_file.userdata_lin.rendered
}

For AWS Windows instances:

data "template_file" "userdata_win" {
  template = <<EOF
<script>
mkdir C:\Users\Administrator\setup-scripts
cd C:\Users\Administrator\setup-scripts
echo "" > _INIT_STARTED_
echo ${base64encode(file("${path.module}/userdata_win.bat"))} > tmp1.b64 && certutil -decode tmp1.b64 userdata.bat
echo ${base64encode(file("${path.module}/config.json"))} > tmp2.b64 && certutil -decode tmp2.b64 config.json
${file("${path.module}/userdata_win.bat")}
echo "" > _INIT_COMPLETE_
</script>
<persist>false</persist>
EOF
}

# ...

resource "aws_instance" "my_windows_instance" {
  # ...
  user_data = data.template_file.userdata_win.rendered
}