Remote-Exec Provisioners
Terraform's "generic" remote-exec
provisioners execute commands or run scripts on
one of the remote systems that were configured through Terraform.
A given resource in a Terraform configuration may contain multiple provisioners,
and they are executed in the order in which they appear in the resource.
The remote instance and credentials to access it are specified by a
Connection block that is defined either
as part of the provisioner block or in its parent resource block.
Remote-exec provisioners can contain only one of three arguments to define their behavior:
- The
inline
argument specifies a list of command strings to execute on the remote system. Terraform actually concatenates the commands into a temporary script, copies it to the remote system and executes it there. - The
script
argument specifies the path to a single script file on the Terraform system that will be copied to the remote system and then executed there. - The
scripts
argument specifies a list of paths to script files that will be copied to the remote system and executed there (in their listed order).
An Inline Example
To try an example of a remote-exec provisioner, download a new version of the file followers.tf into the Terraform configuration directory you have been using (replace the existing followers.tf). This example performs the same operations that were executed in the user_data example using an "inline" remote-exec provisioner:
resource "openstack_compute_floatingip_associate_v2" "followers" {
count = var.num_followers
floating_ip = openstack_networking_floatingip_v2.followers[count.index].address
instance_id = openstack_compute_instance_v2.followers[count.index].id
connection {
type = "ssh"
host = openstack_networking_floatingip_v2.followers[count.index].address
user = "ubuntu"
agent = false
private_key = file(var.private_key_file)
}
provisioner "remote-exec" {
inline = [
"sudo apt-get update > terratest.log",
"sudo echo ${openstack_networking_floatingip_v2.leader.address} > leader_fip.txt",
"echo '* libraries/restart-without-asking boolean true' | sudo debconf-set-selections",
"sudo apt-get -y install python3-pip >> terratest.log",
"pip3 install numpy >> terratest.log"
]
}
}
This version of followers.tf
removes the user_data
argument
that may have been in the file if you worked through the user_data example.
It then adds a "connection" block (line 6) and a provisioner "remote-exec"
block (line 14) in the "followers" resource.
The file's other Resource blocks are not shown in the code excerpt above.
The provisioner block defines the inline
argument (line 15)
to specify all of the commands to execute on the remote system.
These commands will perform a system update, store the floating IP address of the leader instance in a file,
change a system setting, install pip3 and install the numpy package,
just as was done with the user_data
example.
In this case, some commands must be performed with "sudo" and
all files are created in the default user's home directory (the default location for relative paths).
If you previously ran the example for user_data, then your follower instances have already had these provisioning steps performed on them. In that case you should ask Terraform to replace any follower instances so their provisioning is performed using this remote-exec provisioner. Use a command like this:
terraform apply -var-file=terratest.tfvars -replace=openstack_compute_instance_v2.followers[0]
A Script Example
The list of commands in the example above is not too large, but if it were, you might prefer to
store those commands in a separate script file and use the remote-exec provisioner's script
argument instead.
To try an example of doing this, download a new version of the file followers.tf and
the file install_numpy.sh into your Terraform configuration directory.
This version of "followers.tf" contains a provisioner with a script
argument rather than an inline
argument:
provisioner "remote-exec" {
script = "install_numpy.sh"
}
Terraform will copy the file "install_numpy.sh" (below) to your instance and execute it there. Note that the script does not include the command to save the leader's floating IP address to a file, which was included in the other "follower" examples. This is because that command references a Terraform attribute that can only be evaluated on the Terraform system, not on an instance.
#!/bin/bash
sudo apt-get update > terratest.log
echo '* libraries/restart-without-asking boolean true' | sudo debconf-set-selections
sudo apt-get -y install python3-pip > terratest.log
pip3 install numpy >> terratest.log
An alternative approach would be to use a script template file
(discussed with file provisioners)
to save the IP address into a temporary script that would then be copied to the remote system.
That script could then be executed with a simple inline remote-exec
provisioner.
To test the script example, run the Terraform command given above at the end of the "Inline" example to redeploy this configuration while forcing a recreation of the follower instance(s).
Possible Issues
It is possible that your remote-exec provisioners will fail for reasons that are hard to determine. Some of the error messages that Terraform displays can help you figure out what is wrong, but if your provisioning script is having problems, it may be due to interactions with your image. Here are some special situations that can cause problems.
- Hardened Images
- Some images may contain system settings that are meant to "harden" them to be more secure.
One common setting disallows the remote execution of scripts stored in the directory
/tmp
. Unfortunately,/tmp
is the default location to which Terraform copies scripts that it wants to execute! If you suspect you may be facing this issue, you can add an argument to your Connection block to specify a different location (and file name) as the target for Terraform's script copy. Thescript_path
argument must specify an existing path and a non-existent file name:script_path = "/home/ubuntu/remote-script.sh"
- Scripts Edited on Windows
- Windows and Linux systems use different character combinations to signify the ends of lines. As such, a shell script written on Windows may fail to function on Linux systems, and the error messages you see during such failures can be hard to interpret. The internet has plenty of suggestions for fixing this problem.