What are provisioners in Terraform and why are they a last resort?
Learn what Terraform provisioners are, the local-exec, remote-exec and file types, why they break the declarative model, and safer alternatives.
Expected Interview Answer
Provisioners in Terraform run scripts or commands on a local or remote machine as part of resource creation or destruction, and they are a last resort because they run imperative steps that Terraform cannot see into, track in state, or reliably re-run, breaking its declarative model.
Terraform normally converges infrastructure by comparing declared state to real state, but a provisioner is a one-time side effect it cannot inspect: if the script half-succeeds, Terraform marks the resource tainted and the only recovery is destroy-and-recreate. HashiCorp recommends using cloud-native mechanisms first — user_data / cloud-init for bootstrapping, purpose-built providers for configuration, and dedicated tools like Ansible for post-provision setup — reserving provisioners for cases with no declarative alternative.
- Bootstrap a machine when no cloud-init path exists
- Run a local command such as generating an inventory file
- Trigger external tooling on create or destroy
- Copy files to a freshly created host via the file provisioner
- Bridge a gap until a proper provider or data source exists
AI Mentor Explanation
Provisioners are like a captain physically running onto the pitch to hand-adjust a fielder's exact stance after the field is already set. The scorebook and field plan know nothing about that manual tweak, so if the fielder mishears and stands wrong, nobody can audit what happened — you just reset the whole placement. That is why coaches prefer clear pre-agreed signals: repeatable, visible, and part of the plan, not last-minute imperative meddling that leaves no trace on the official record.
Step-by-Step Explanation
Step 1
Exhaust declarative options first
Prefer user_data / cloud-init, dedicated providers, or data sources before reaching for any provisioner.
Step 2
Choose the provisioner type
Use local-exec for commands on the machine running Terraform, or remote-exec/file for actions on the created resource.
Step 3
Configure the connection
For remote provisioners, define a connection block with host, user, and credentials so Terraform can reach the resource.
Step 4
Decide the timing
Default provisioners run on create; set when = destroy for cleanup steps that must run before the resource is removed.
Step 5
Handle failure explicitly
Set on_failure = continue if a non-critical step should not taint the resource, and keep scripts idempotent.
What Interviewer Expects
- Knows the difference between local-exec, remote-exec, and file provisioners
- Can explain why provisioners break Terraform's declarative, stateful model
- Mentions cloud-init/user_data and configuration tools as preferred alternatives
- Understands tainting and destroy-time provisioners
- Can describe connection blocks and on_failure behavior
Common Mistakes
- Using provisioners as the primary configuration method instead of a last resort
- Forgetting that a failed provisioner taints the resource and forces recreation
- Writing non-idempotent scripts that break on re-runs
- Omitting the connection block for remote-exec
- Assuming Terraform tracks what the provisioner script actually did
Best Answer (HR Friendly)
“Provisioners let Terraform run scripts on a machine while building infrastructure, like a setup command after a server is created. They are a last resort because Terraform can't see what the script does, so teams prefer built-in cloud setup options that are safer and repeatable.”
Code Example
resource "aws_instance" "web" {
ami = "ami-0abcd1234"
instance_type = "t3.micro"
# Prefer this declarative path when possible
user_data = file("cloud-init.yaml")
# Last-resort provisioner
provisioner "remote-exec" {
inline = [
"sudo apt-get update",
"sudo apt-get install -y nginx",
]
connection {
type = "ssh"
host = self.public_ip
user = "ubuntu"
private_key = file("~/.ssh/id_rsa")
}
on_failure = fail
}
}Follow-up Questions
- What is the difference between local-exec and remote-exec provisioners?
- How does a destroy-time provisioner differ from a create-time one?
- Why does a failed provisioner taint a resource?
- How would you bootstrap an instance without any provisioner?
- What is the null_resource with triggers pattern used for?
MCQ Practice
1. Why are provisioners considered a last resort in Terraform?
Provisioners run imperative steps outside Terraform's state model, so their effects cannot be tracked, diffed, or reliably re-run.
2. Which provisioner runs a command on the machine executing Terraform rather than the created resource?
local-exec runs on the host running Terraform; remote-exec and file act on the target resource over a connection.
3. What happens when a create-time provisioner fails with the default on_failure setting?
By default on_failure = fail, so a failed provisioner taints the resource, and the next apply destroys and recreates it.
Flash Cards
What are the three main provisioner types? — local-exec (runs on the Terraform host), remote-exec (runs on the resource), and file (copies files to the resource).
Why are provisioners a last resort? — They run untracked imperative steps that Terraform cannot inspect, diff, or reliably re-run, breaking the declarative model.
What is the preferred alternative for bootstrapping an instance? — Cloud-native user_data / cloud-init, dedicated providers, or configuration tools like Ansible.
What does on_failure = continue do? — It lets Terraform proceed without tainting the resource when a non-critical provisioner step fails.
Continue Learning
Related Interview Questions
What is the difference between Terraform and configuration tools like Ansible?
medium
What is the Terraform state file and why is it important?
easy
What is Terraform and what problem does infrastructure as code solve?
easy
What is declarative vs imperative infrastructure and where does Terraform fit?
medium