How to Use Provisioner in Terraform: Syntax and Examples
In Terraform, a
provisioner runs scripts or commands on a resource after it is created or before it is destroyed. You add a provisioner block inside a resource block to specify actions like running shell commands or copying files.Syntax
A provisioner block is placed inside a resource block. It has a type like local-exec or remote-exec and contains configuration for the commands or scripts to run.
- local-exec: Runs commands on the machine running Terraform.
- remote-exec: Runs commands on the remote resource via SSH or WinRM.
- when: Optional, controls if provisioner runs
createordestroy.
terraform
resource "aws_instance" "example" { ami = "ami-12345678" instance_type = "t2.micro" provisioner "local-exec" { command = "echo Hello from Terraform" } provisioner "remote-exec" { inline = [ "sudo apt-get update", "sudo apt-get install -y nginx" ] connection { type = "ssh" user = "ubuntu" private_key = file("~/.ssh/id_rsa") host = self.public_ip } } }
Example
This example creates an AWS EC2 instance and uses a remote-exec provisioner to install Nginx on it after creation. It connects via SSH using the instance's public IP.
terraform
provider "aws" { region = "us-east-1" } resource "aws_instance" "web" { ami = "ami-0c55b159cbfafe1f0" # Ubuntu Server 20.04 LTS instance_type = "t2.micro" provisioner "remote-exec" { inline = [ "sudo apt-get update", "sudo apt-get install -y nginx" ] connection { type = "ssh" user = "ubuntu" private_key = file("~/.ssh/id_rsa") host = self.public_ip } } }
Output
Terraform will create the EC2 instance, then connect via SSH and run the commands to update packages and install Nginx.
Common Pitfalls
- Provisioners run only after resource creation; they do not run on updates unless the resource is replaced.
- Using provisioners for configuration management is discouraged; use dedicated tools like Ansible or cloud-init instead.
- Incorrect connection settings cause
remote-execto fail. - Provisioners can cause Terraform runs to hang if commands do not finish or fail silently.
terraform
resource "aws_instance" "bad_example" { ami = "ami-12345678" instance_type = "t2.micro" provisioner "remote-exec" { inline = ["sudo apt-get update"] connection { type = "ssh" user = "wrong-user" # Incorrect user causes failure host = self.public_ip } } } # Corrected connection block resource "aws_instance" "good_example" { ami = "ami-12345678" instance_type = "t2.micro" provisioner "remote-exec" { inline = ["sudo apt-get update"] connection { type = "ssh" user = "ubuntu" private_key = file("~/.ssh/id_rsa") host = self.public_ip } } }
Quick Reference
Use local-exec to run commands on your local machine and remote-exec to run commands on the created resource. Always configure the connection block correctly for remote commands. Avoid using provisioners for complex setup; prefer dedicated configuration tools.
| Provisioner Type | Description | Typical Use Case |
|---|---|---|
| local-exec | Runs commands on the machine running Terraform | Run local scripts or commands after resource creation |
| remote-exec | Runs commands on the remote resource via SSH or WinRM | Install software or configure remote servers |
| file | Copies files to remote machines | Transfer configuration files or scripts |
Key Takeaways
Add provisioner blocks inside resource blocks to run scripts or commands after resource creation.
Use local-exec for local commands and remote-exec for commands on remote resources via SSH or WinRM.
Always configure connection details correctly for remote-exec to work.
Provisioners should be a last resort; prefer configuration management tools for complex setups.
Provisioners run only on resource creation or destruction, not on updates.