Course outlines for learning terraform.
remote-exec| expected time | requirements |
|---|---|
| 60 minutes | A computer with Terraform installed, terraform knowledge. |
Goal: Be able to make use of remote-exec when required.
The remote-exec provisioner executes a command on the remote resource after creating the resource. This is typically applied when spinning up a machine.
A few use-cases:
Generally speaking; try to limit the commands to the bare minimum to get going; there are better ways to configure a system.
For Azure virtual machines, there is also a resource azurerm_virtual_machine_extension which is more suitable to execute commands. Since most other cloud providers do not have such a resource, understanding remote-exec is quite useful.
For Google instances, there is a parameter metadata_startup_script in the resource compute_instance, that does something similar.
There are a few ways to execute commands on the remote host.
The remote-exec provisioner may need extra details on how to connect to the resource, you’ll see them in the examples below.
These extra details include the host, which refers to the public ip address. In azure the public IP address is azurerm_network_interface resource, but only become available after the machine is created. Therefor a bit of dark magic is required: depends_on:
First, create a data block to find the IP address:
data "azurerm_public_ip" "pip" {
name = azurerm_public_ip.pip.name
resource_group_name = azurerm_resource_group.rg.name
depends_on = [azurerm_virtual_machine.vm, ]
}
Next let the null_resource depend on the block above:
For a (short list of) command(s), inline can be used.
resource "null_resource" "default" {
depends_on = [data.azurerm_public_ip.pip, ]
connection {
host = data.azurerm_public_ip.pip.ip_address
user = "my_user"
password = "Som3-P4s$W0rd."
}
provisioner "remote-exec" {
inline = [
"sudo apt-get update",
"sudo apt-get upgrade -y"
]
}
}
When you need to run a single script, that likely contains many commands, you can use script. The script is copied to the remote resource and executed.
resource "null_resource" "default" {
depends_on = [data.azurerm_public_ip.pip, ]
connection {
host = data.azurerm_public_ip.pip.ip_address
user = "my_user"
password = "Som3-P4s$W0rd."
}
provisioner "remote-exec" {
script = "my_script.sh"
}
}
If there are multiple scripts to be executed, scripts can be used:
resource "null_resource" "default" {
depends_on = [data.azurerm_public_ip.pip, ]
connection {
host = data.azurerm_public_ip.pip.ip_address
user = "my_user"
password = "Som3-P4s$W0rd."
}
provisioner "remote-exec" {
scripts = [
"my_script_1.sh",
"my_script_2.sh"
]
}
}
If you need to always run a script, you can use a pattern like this:
resource "null_resource" "default" {
depends_on = [data.azurerm_public_ip.pip, ]
connection {
host = data.azurerm_public_ip.pip.ip_address
user = "my_user"
password = "Som3-P4s$W0rd."
}
# This trigger makes sure to ALWAYS run the provisioner, not very idempotent...
triggers = {
always_run = "${timestamp()}"
}
provisioner "remote-exec" {
inline = [
"sudo apt-get update",
"sudo apt-get upgrade -y"
]
}
}
azurerm_virtual_machine and have update all packages. (Two commands: sudo apt-get update and sudo apt-get upgrade -y)null_resource is stored in the state.compute_instance and have all packages updated. (apt-get update and apt-get upgrade -y).metadata_startup_script is stored in the state.See this repository.