Terraform provisioners are a set of tools within the Terraform infrastructure-as-code (IaC) tool that allow you to perform actions on the local machine or remote resources during the provisioning or destruction phases of infrastructure deployment. Provisioners can be useful for tasks that are not directly supported by Terraform’s declarative language, such as running scripts, executing commands, or performing configuration management tasks.
Table of Contents
Types of Terraform Provisioners, Implementing local and remote-exec Provisioners:
In Terraform, the local-exec
and remote-exec
provisioners are used to execute commands during the provisioning process. These provisioners allow you to run scripts or commands on either the local machine (where Terraform is executed) or on remote resources (using SSH or WinRM).
1.Local-Exec Provisioner:
The local-exec
provisioner runs commands on the machine where Terraform is executed. It is typically used for tasks that are local to the machine running Terraform, such as initializing resources, configuring local services, or generating files.
Here is an example of using the local-exec
provisioner
resource "aws_instance" "myec2" {
ami = "ami-082b5a644766e0e6f"
instance_type = "t2.micro"
provisioner "local-exec" {
command = "echo ${aws_instance.myec2.private_ip} >> private_ips.txt"
}
}
2.Remote-Exec Provisioner:
The remote-exec
provisioner, on the other hand, runs commands on a remote resource, typically using SSH (for Linux) or WinRM (for Windows). This is useful for tasks such as installing software, configuring services, or performing other actions on the remote server.
Here’s an example of using the remote-exec
provisioner
SSH Prerequisites:
- SSH Key Pair: For SSH connections, you need an SSH key pair. The private key should be available on the machine running Terraform, and the public key should be added to the authorized_keys file on the target resource.
- Firewall Rules: Ensure that the firewall rules on the target resource allow SSH traffic (port 22 by default) from the machine running Terraform.
- SSH Agent (Optional): If using an SSH agent for key management, make sure it is running and the appropriate keys are added.
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
# Configure the AWS Provider
provider "aws" {
region = "ap-south-1"
}
resource "aws_instance" "example" {
ami = "ami-0a0f1259dd1c90938" # Specify the AMI ID for your desired Linux distribution
instance_type = "t2.micro" # Change this to your desired instance type
key_name = "demo" # Replace with the name of the key pair created in Step 1
connection {
type = "ssh"
user = "ec2-user" # Default user for Amazon Linux. For other distributions, use the appropriate username (e.g., ubuntu, centos)
private_key = file("./demo.pem") # Replace with the path to your downloaded .pem file
host = aws_instance.example.public_ip
}
provisioner "remote-exec" {
inline = [
"sudo yum install -y nginx",
"sudo yum update -y",
"sudo amazon-linux-extras intall -y ngnix1",
"sudo systemctl start nginx"
]
on_failure = continue
}
tags = {
Name = "example"
}
}
following output will be shown in cli after terraform apply command run.

In this example, when Terraform creates the AWS EC2 instance, it will connect to the instance using SSH and execute the specified commands to update the package list and install Nginx.
Browsing public ip of created instance we can see below message.

Reference link : https://developer.hashicorp.com/terraform/language/resources/provisioners/local-exec
Creation-Time & Destroy-Time Provisioners :
In Terraform, provisioners can be categorized based on when they are executed during the lifecycle of a resource: creation-time and destroy-time provisioners.
1.Creation-Time Provisioners:
Creation-time provisioners are executed during the creation or provisioning of a resource. They are used to perform actions on a resource after it has been created but before it is fully ready for use. Common use cases for creation-time provisioners include initializing software on a newly created machine or configuring a resource immediately after its creation.
provider "aws" {
region = "ap-southeast-1"
access_key = "YOUR-KEY"
secret_key = "YOUR-KEY"
}
resource "aws_security_group" "allow_ssh" {
name = "allow_ssh"
description = "Allow SSH inbound traffic"
ingress {
description = "SSH into VPC"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
description = "Outbound Allowed"
from_port = 0
to_port = 65535
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
}
resource "aws_instance" "myec2" {
ami = "ami-0b1e534a4ff9019e0"
instance_type = "t2.micro"
key_name = "ec2-key"
vpc_security_group_ids = [aws_security_group.allow_ssh.id]
provisioner "remote-exec" {
inline = [
"sudo yum -y install nano"
]
}
provisioner "remote-exec" {
when = destroy
inline = [
"sudo yum -y remove nano"
]
}
connection {
type = "ssh"
user = "ec2-user"
private_key = file("./ec2-key.pem")
host = self.public_ip
}
}
In this example, the local-exec
provisioner creates a file on the local machine, and the remote-exec
provisioner runs commands on the newly created AWS EC2 instance. Both of these provisioners are executed during the creation of the resource.
In Terraform, when a creation-time provisioner fails, Terraform marks the associated resource as “tainted.” This means that the resource is considered as potentially being in an inconsistent or unrecoverable state. The tainted resource will be destroyed and recreated during the next terraform apply
. The process of marking a resource as tainted is part of Terraform’s attempt to handle failures gracefully.
2.Destroy-Time Provisioners:
Destroy-time provisioners are executed during the destruction or deletion of a resource. They are used to perform cleanup actions or to gather information before a resource is destroyed. Common use cases for destroy-time provisioners include removing resources from load balancers, taking snapshots, or backing up data before deletion.
In this example, the additional remote-exec
provisioner with when = "destroy"
will only be executed during the destruction of the resource. It creates a file indicating that the resource is being destroyed.
Failure Behavior for Terraform Provisioners:
In Terraform, you can control the behavior of provisioners when they encounter failures using the on_failure
attribute. This attribute specifies what action Terraform should take when a provisioner fails to execute successfully. There are two primary values for the on_failure
attribute: “continue” and “fail.”
1. Continue (Soft Fail):
If the on_failure
attribute is set to “continue,” it means that Terraform will treat the failure of a provisioner as a soft fail. In the case of a soft fail, Terraform will mark the associated resource as tainted but will continue with the execution of subsequent resources and provisioners. The provisioning process will not be halted entirely.
2. Fail (Hard Fail):
If the on_failure
attribute is set to “fail,” it means that Terraform will treat the failure of a provisioner as a hard fail. In the case of a hard fail, Terraform will stop the provisioning process immediately, and the entire apply operation will be aborted. The associated resource will be marked as tainted, indicating that it is potentially in an inconsistent state.
Example:
provider "aws" {
region = "ap-southeast-1"
access_key = "YOUR-KEY"
secret_key = "YOUR-KEY"
}
resource "aws_security_group" "allow_ssh" {
name = "allow_ssh"
description = "Allow SSH inbound traffic"
ingress {
description = "SSH into VPC"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
}
resource "aws_instance" "myec2" {
ami = "ami-0b1e534a4ff9019e0"
instance_type = "t2.micro"
key_name = "ec2-key"
vpc_security_group_ids = [aws_security_group.allow_ssh.id]
provisioner "remote-exec" {
on_failure = continue
inline = [
"sudo yum -y install nano"
]
}
connection {
type = "ssh"
user = "ec2-user"
private_key = file("./ec2-key.pem")
host = self.public_ip
}
}
Conclusion:
It’s important to choose the appropriate on_failure
behavior based on the criticality of the provisioner and your desired error-handling strategy. Soft fails can be useful when you want to continue with the provisioning of other resources even if one fails, while hard fails may be necessary for critical steps where failure should halt the entire process to prevent further issues.
Related Articles: