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. The script_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.
 
©  |   Cornell University    |   Center for Advanced Computing    |   Copyright Statement    |   Inclusivity Statement