Learn how to execute multiple commands on your remote instances using Terraform's remote-exec provisioner with practical examples and workarounds.
Terraform's remote-exec provisioner is a powerful tool for running commands on your resources, but it's essential to understand its limitations. A key point is that remote-exec runs only once during a resource's creation. This means if you modify the commands within remote-exec after the resource is already up and running, Terraform won't automatically re-execute those commands on the existing resource.
Terraform's remote-exec provisioner runs only once when a resource is created. If you modify the commands within remote-exec after the resource exists, Terraform won't re-run them automatically.
Let's say you have this initial setup:
resource "aws_instance" "example" {
ami = "ami-0c94855ba95c574c9"
instance_type = "t2.micro"
provisioner "remote-exec" {
inline = ["echo 'Initial setup' > /tmp/setup.txt"]
}
}If you later change the inline command to:
inline = ["echo 'Updated setup' > /tmp/setup.txt"] Terraform won't update /tmp/setup.txt on the existing instance.
Here's why and how to handle such situations:
Idempotency: Terraform strives to be idempotent. It aims to bring your infrastructure to the desired state without repeating actions unnecessarily. Since the resource already exists, Terraform assumes the initial provisioning was successful and doesn't re-run it.
Resource Lifecycle: Provisioners are tightly coupled to the lifecycle of a resource. They primarily run during the creation phase.
Solutions:
Resource Recreation: The most straightforward approach is to:
terraform destroy targeting the specific resource).terraform apply) which will recreate the resource and execute the updated remote-exec commands.null_resource with Triggers: For more granular control, use the null_resource in conjunction with triggers:
resource "null_resource" "setup" {
triggers = {
commands = filemd5("/path/to/your/setup/script.sh")
}
provisioner "remote-exec" {
inline = ["bash /path/to/your/setup/script.sh"]
}
}triggers block ensures the remote-exec runs whenever the content of your script changes. The filemd5 function calculates the MD5 checksum of the script, triggering the null_resource to re-provision when the checksum changes.Important Considerations:
This code example illustrates the problem of Terraform's remote-exec not executing again when resources are updated. It presents two solutions: recreating the resource and using a null_resource. The first solution involves deleting and recreating the resource to trigger the remote-exec provisioner. The second solution utilizes a null_resource with a remote-exec provisioner and a trigger based on the script's hash. Modifying the script triggers the null_resource to re-run the remote-exec. The choice between these solutions depends on the specific requirements and trade-offs of the situation.
This example demonstrates the issue of remote-exec not re-running on resource updates and provides solutions using resource recreation and null_resource.
Initial Setup (main.tf):
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.0"
}
}
}
provider "aws" {
region = "us-west-2" # Replace with your desired region
}
resource "aws_instance" "example" {
ami = "ami-0c94855ba95c574c9" # Replace with a suitable AMI ID
instance_type = "t2.micro"
provisioner "remote-exec" {
inline = ["echo 'Initial setup' > /tmp/setup.txt"]
}
}1. Resource Recreation:
terraform apply to create the initial instance.inline command in aws_instance.example to:
inline = ["echo 'Updated setup' > /tmp/setup.txt"]terraform apply. You'll notice Terraform doesn't update the file.terraform destroy (or target the specific instance).terraform apply again. This recreates the instance, and the updated command runs.2. Using null_resource (main.tf):
resource "aws_instance" "example" {
ami = "ami-0c94855ba95c574c9" # Replace with a suitable AMI ID
instance_type = "t2.micro"
}
resource "null_resource" "setup" {
provisioner "remote-exec" {
connection {
type = "ssh"
user = "ubuntu" # Replace with your instance user
private_key = file("~/.ssh/your-key.pem") # Replace with your private key path
host = aws_instance.example.public_ip
}
inline = ["bash /tmp/setup.sh"]
}
triggers = {
script_hash = filemd5("/tmp/setup.sh") # Replace with your script path
}
}setup.sh:
#!/bin/bash
echo "Updated setup from script" > /tmp/setup.txtterraform apply.setup.sh.terraform apply again. The null_resource detects the script change and re-runs the remote-exec.Remember:
remote-exec connection. Adjust accordingly if you're using a different method.null_resource approach provides more control but adds complexity. Choose the solution that best suits your needs.This document explains that Terraform's remote-exec provisioner is not intended for ongoing configuration management. It runs only once when a resource is created, and subsequent changes to the remote-exec block won't be applied automatically.
Here are some additional points to consider:
Why this behavior is desirable:
When to use remote-exec:
When to avoid remote-exec:
remote-exec. A mistake during provisioning could lead to data loss or service disruption.Alternatives to resource recreation:
remote-exec for gathering data.Best Practices:
terraform plan, terraform validate) to catch potential issues before applying changes.By understanding the limitations and appropriate use cases of remote-exec, you can leverage Terraform effectively while maintaining a predictable and manageable infrastructure.
Problem: Terraform's remote-exec provisioner runs only once during resource creation. Modifying remote-exec commands after resource creation won't trigger re-execution, leaving your infrastructure in a potentially outdated state.
Why?
Solutions:
Resource Recreation:
terraform destroy to remove the existing resource.terraform apply, triggering remote-exec on the newly created resource.null_resource with Triggers:
null_resource for more controlled re-provisioning.triggers based on file changes (e.g., using filemd5) to automatically trigger remote-exec when your scripts are modified.resource "null_resource" "setup" {
triggers = {
commands = filemd5("/path/to/your/setup/script.sh")
}
provisioner "remote-exec" {
inline = ["bash /path/to/your/setup/script.sh"]
}
}Important Considerations:
Terraform's remote-exec provisioner, while useful for initial resource setup, has limitations due to its one-time execution nature. It runs only during resource creation and modifications to the remote-exec block won't apply to existing resources. This behavior stems from Terraform's emphasis on idempotency and predictable infrastructure management. For simple, one-time configurations like initial package installations or script executions, remote-exec proves beneficial. However, for complex, ongoing configuration management, dedicated tools like Ansible, Chef, or Puppet are recommended. When updates are needed, consider resource recreation by deleting and re-creating the resource, which triggers remote-exec on the new instance. Alternatively, for more granular control, leverage the null_resource with triggers based on file changes, ensuring remote-exec runs when your scripts are modified. Remember to manage your Terraform state file diligently and explore alternative approaches like data sources when fetching information from resources post-creation. By understanding these nuances and best practices, you can effectively utilize Terraform's remote-exec provisioner while maintaining a predictable and manageable infrastructure.
How to completely reverse, destroy ALL resources if a local or ... | The documentation for āProvisionersā and āTaint (deprecated)ā state āTerraform does this [taints a resource] because a failed provisioner can leave a resource in a semi-configured stateā and ā⦠other users could create a new plan against your tainted object before you can review the effectsā, respectively. Both of these, exactly, are happening to us, and Iām looking for guidance on how to address. We spin up multiple VMs (nodes) for HA Kubernetes from scratch and use kubeadm and other basic, n...
Terraform, Provisioners, Connections, and Static Nodes - DEV ... | Nov 23, 2018 ... It can get a little confusing sometimes, at least it's gotten me more than ... That provisioner looks like this. provisioner "remote-exec" {Ā ...
How to escape double-quotes in the local-exec provisioner - Terraform | Hi, everybody, Iām trying to execute a command with the Terraform ālocal-execā provisioner but this command contains double quotes (") and I canāt escape them to run the command correctly. I have reduced my problem to a simpler one, here is my code : resource "null_resource" "Hello_World" { provisioner "local-exec" { command = "echo "Hello World"" } } I would like the result of this command to be : "Hello World" But the result is : "Hello World" Execution : null_resource.Hel...