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.
<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.
terraform plan
, encode whatever local files you need as base64 strings using terraform functions base64encode(file("path/to/file"))
._INIT_STARTED_
) at the start of userdata execution; this file will have the creation timestamp of when the userdata
execution started.userdata
script itself (userdata_win.bat
or userdata_lin.sh
)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.)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
}
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
}