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.