🧠 Terraform Provisioners: Run Scripts (local-exec, remote-exec) During Deployment


Terraform is an Infrastructure as Code (IaC) tool that automates the creation and management of cloud resources. While Terraform primarily focuses on declaring infrastructure, sometimes you need to run commands or scripts on your local machine or remote servers during or after resource creation.

This is where Terraform Provisioners come in.


💡 Simple Definition

Provisioners in Terraform allow you to execute scripts or commands on your local system or remote servers during the resource creation or destruction process.

They bridge the gap between infrastructure provisioning and software configuration.


🔹 Common Use Cases

  • Bootstrapping an instance (e.g., installing software packages)
  • Configuring applications after deployment
  • Executing initialization scripts
  • Copying configuration files to remote machines
  • Cleaning up resources during destruction

🔹 Types of Terraform Provisioners

Terraform provides several types of provisioners:

ProvisionerDescription
local-execRuns a command locally on the machine where Terraform is executed
remote-execRuns commands remotely on a resource (like a VM) via SSH or WinRM
fileUploads a file or directory to a remote resource
null_resourceActs as a placeholder for running provisioners without creating actual resources

🔹 — Provisioner Flow

Terraform Plan

Terraform Apply

Resource Created

Provisioner Triggered

local-exec runs locally

remote-exec runs remotely

file uploads files

Script Execution Result

Provisioning Complete

Explanation: When Terraform creates a resource, the associated provisioners are triggered. They execute scripts locally or remotely and then return the execution status back to Terraform.


🔹 Syntax of a Provisioner

Here’s the general syntax used inside a resource block:

resource "aws_instance" "example" {
ami = "ami-123456"
instance_type = "t2.micro"
provisioner "local-exec" {
command = "echo 'Instance Created Successfully!'"
}
}

🔹 Example 1: Using local-exec Provisioner

The local-exec provisioner runs a command on the local machine where Terraform is executed. It’s ideal for tasks like logging, API calls, or triggering CI/CD steps.

Example Code

provider "aws" {
region = "us-east-1"
}
resource "aws_instance" "web" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
provisioner "local-exec" {
command = "echo 'Deployed ${self.id} to AWS' >> deploy_log.txt"
}
}

Explanation:

  • The local-exec runs on your local terminal.
  • ${self.id} dynamically refers to the instance’s ID.
  • The result is saved in a log file deploy_log.txt.

🧩 Output Example

Terminal window
$ terraform apply
aws_instance.web: Creating...
aws_instance.web: Provisioning with 'local-exec'...
Deployed i-09f1a34f28f to AWS

🔹 Example 2: Using remote-exec Provisioner

The remote-exec provisioner allows you to execute commands on the remote server after it’s created. This is commonly used for bootstrapping software installations or running setup scripts.

Example Code

provider "aws" {
region = "us-east-1"
}
resource "aws_instance" "app_server" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
key_name = "my-key"
provisioner "remote-exec" {
inline = [
"sudo apt-get update -y",
"sudo apt-get install -y nginx",
"sudo systemctl start nginx"
]
connection {
type = "ssh"
user = "ubuntu"
private_key = file("~/.ssh/my-key.pem")
host = self.public_ip
}
}
}

Explanation:

  • Terraform connects to the instance using SSH.
  • It installs and starts Nginx.
  • You can use this to perform post-deployment configuration.

🧩 Output Example

Terminal window
$ terraform apply
aws_instance.app_server: Provisioning with 'remote-exec'...
Installing Nginx...
Nginx started successfully!

🔹 Example 3: Using file Provisioner

The file provisioner is used to copy files or directories from your local system to a remote instance.

Example Code

provider "aws" {
region = "us-east-1"
}
resource "aws_instance" "config_server" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
key_name = "my-key"
provisioner "file" {
source = "app_config.txt"
destination = "/home/ubuntu/app_config.txt"
connection {
type = "ssh"
user = "ubuntu"
private_key = file("~/.ssh/my-key.pem")
host = self.public_ip
}
}
provisioner "remote-exec" {
inline = [
"cat /home/ubuntu/app_config.txt",
"echo 'Configuration Deployed!'"
]
connection {
type = "ssh"
user = "ubuntu"
private_key = file("~/.ssh/my-key.pem")
host = self.public_ip
}
}
}

Explanation:

  • The file provisioner uploads app_config.txt to the remote server.
  • The remote-exec then verifies and prints the contents.

🔹 Example 4: Combining Provisioners with Null Resource

When you want to run scripts without creating actual resources, use null_resource.

resource "null_resource" "run_script" {
provisioner "local-exec" {
command = "bash ./scripts/setup_env.sh"
}
}

Use Case: This approach is great for CI/CD jobs, database migrations, or local validations.


🔹 Lifecycle Behavior

Provisioners are executed during the creation or destruction of a resource.

Execution Order:

  1. Terraform creates the resource.
  2. Runs the provisioner (local or remote).
  3. Marks the resource as “created.”

You can control when provisioners run using when:

provisioner "local-exec" {
when = "destroy"
command = "echo 'Cleaning up resource...'"
}

This runs the command before destroying the resource.


🔹 — Execution Order

Local SystemAWS InstanceTerraformLocal SystemAWS InstanceTerraformCreate InstanceInstance Createdlocal-exec runs (e.g., echo log)remote-exec runs (install software)file provisioner (upload configs)Provisioning Completed


🔹 How to Remember Provisioners (Mnemonic)

Use the acronym “L.R.F”Local, Remote, File 🧠

LetterMeaningFunction
LLocal-execRuns commands locally
RRemote-execRuns commands remotely
FFileUploads files to remote hosts

💡 Memory Trick:

“Provisioners help Terraform Link, Run, and File your configurations during deployments.”


🔹 Terraform Provisioners in Interview and Exams

🎯 Frequently Asked Questions

Q1. What are Terraform provisioners used for? 👉 To execute local or remote scripts during resource creation or destruction.

Q2. Difference between local-exec and remote-exec? 👉 local-exec runs on the Terraform host; remote-exec runs inside the provisioned resource.

Q3. What is a null_resource? 👉 A dummy resource used to run provisioners without creating actual cloud resources.

Q4. Are provisioners recommended in production? 👉 Only when necessary — Terraform recommends configuration management tools (like Ansible) for heavy software setup.

Q5. How to handle errors in provisioners? 👉 Use on_failure = continue to prevent Terraform from halting execution.


🧠 Exam & Study Tip

Use this phrase to memorize:

“Provisioners run code while Terraform builds nodes.”

To remember:

  • Local-exec → Local machine tasks
  • Remote-exec → SSH/WinRM commands
  • File → Copy configuration files

And always remember: Provisioners are last-resort tools — not the main automation mechanism.


🔹 Why It’s Important to Learn Terraform Provisioners

ReasonExplanation
Bridges IaC and AutomationLets Terraform trigger scripts and commands automatically
Post-deployment configurationInstall or configure software right after instance creation
FlexibilityRuns both local and remote tasks seamlessly
Real-world useUsed in multi-step infrastructure setups
Exam relevanceTerraform Associate Exam includes questions on provisioner usage
CI/CD IntegrationHelps integrate Terraform into pipelines by executing hooks

🔹 Best Practices

Best PracticeDescription
Use provisioners sparinglyPrefer configuration tools (Ansible, Chef, Puppet)
Handle errors gracefullyUse on_failure = continue
Avoid sensitive data exposureDon’t echo secrets into logs
Use depends_on to control orderEnsure correct resource dependencies
Combine with null_resourceFor script-only automation
Document scriptsMaintain clear README for team understanding

🔹 Common Mistakes to Avoid

MistakeWhy It’s ProblematicSolution
Overusing provisionersMakes Terraform impure and state-heavyLimit usage to bootstrapping
Ignoring SSH issuesFails remote-execValidate SSH keys before use
No error handlingTerraform halts unexpectedlyAdd on_failure parameter
Hardcoded pathsReduces portabilityUse variables or path.module
Unnecessary dependenciesSlows down buildsSimplify workflow

🔹 Real-World Use Cases

  1. Cloud Bootstrapping:

    • Automatically install web servers or dependencies.
  2. File Uploading:

    • Send configuration templates or credentials securely.
  3. CI/CD Integration:

    • Trigger Jenkins or GitHub Actions pipelines after deployments.
  4. Security Audits:

    • Execute compliance validation scripts post-deployment.
  5. Hybrid Infrastructure:

    • Configure both on-prem and cloud nodes via provisioners.

🔹 Summary Table

ProvisionerRuns OnUsage ExampleTypical Use Case
local-execLocal Machineecho "Done"CI/CD hooks, logs
remote-execRemote Serverapt install nginxBootstrapping servers
fileRemote ServerUpload config filesDeploying configurations
null_resourceNoneRun local scriptsScript automation

🔹 Quick Demo Workflow

Terminal window
terraform init
terraform validate
terraform plan
terraform apply

Observe the log output — you’ll see provisioners executing commands during deployment.


🔹 Summary Diagram

Terraform Apply

Create Resources

local-exec

remote-exec

file

Run Local Command

Run Remote Script

Upload Files

Provisioning Done


Terraform Provisioners act as the “final step” between infrastructure creation and environment readiness. They allow you to:

  • Execute local commands (local-exec)
  • Run remote setup scripts (remote-exec)
  • Transfer files (file)
  • Trigger custom automation (null_resource)

However, Terraform is not a configuration management tool — use provisioners only for minimal bootstrapping or integration hooks.

💬 “Provisioners are Terraform’s glue — connecting your infrastructure with automation logic.”

By mastering provisioners, you gain the flexibility to automate any deployment task, ensuring your cloud infrastructure is both ready and reliable.