👋 Hey there, I’m Dheeraj Choudhary an AI/ML educator, cloud enthusiast, and content creator on a mission to simplify tech for the world.
After years of building on YouTube and LinkedIn, I’ve finally launched TechInsight Neuron a no-fluff, insight-packed newsletter where I break down the latest in AI, Machine Learning, DevOps, and Cloud.
🎯 What to expect: actionable tutorials, tool breakdowns, industry trends, and career insights all crafted for engineers, builders, and the curious.
🧠 If you're someone who learns by doing and wants to stay ahead in the tech game you're in the right place.

What Are Terraform Provisioners?

Provisioners are blocks inside a Terraform resource that run scripts or commands during terraform apply or destroy.
They come in two types:
Type | Purpose |
|---|---|
| Runs a command on your local machine |
| Runs a command on the provisioned resource |
local-exec Provisioner
Executes a command on your local machine, typically for things like:
Triggering Ansible or Bash scripts
Running
curl,scp,awsCLINotifying other services or systems
🔧 Example:
resource "null_resource" "notify" {
provisioner "local-exec" {
command = "echo Deployment complete!"
}
}Output:
null_resource.notify: Provisioning with 'local-exec'...
Deployment complete!remote-exec Provisioner

Runs commands on the remote resource, like an EC2 instance.
Requires connection details like SSH key, user, and host.
🔧 Example:
resource "aws_instance" "web" {
ami = "ami-0c55b159cbfafe1f0"
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
}
}
Using file Provisioner
Terraform also lets you copy files to a remote machine:
provisioner "file" {
source = "startup.sh"
destination = "/tmp/startup.sh"
}
Requires a working connection block.
⚠️ Why Provisioners Are a Last Resort
HashiCorp officially states that provisioners should only be used as a last resort. Why?
They break idempotency: If the resource is unchanged but the script changes, Terraform won’t know to rerun it.
They cause dependency issues: Terraform doesn’t track provisioners in its state.
They’re fragile: SSH failures, connection timeouts, or unreachable hosts can break the entire apply process.
They’re hard to debug: Logs are embedded in apply output, and re-running them isn’t easy.
✅ When to Use Provisioners (Cautiously)
Initial setup where you must install something right after creation
Lightweight file copy or bootstrap scripts
Triggering external tools or notifications
❌ When to Avoid Them
Long-running or complex scripts → use Ansible, Chef, or user-data
Repetitive provisioning logic → use
cloud-initor PackerAnything you can do with native Terraform resources
Provisioning infrastructure in CI/CD pipelines
🔁 Safer Alternatives

Use Case | Better Option |
|---|---|
VM bootstrapping | Use |
Config management | Use Ansible/Chef/Puppet |
File deployment | Use baked AMIs or |
Event triggering | Use webhooks / external tools |
Post-deployment orchestration | Run from CI/CD or external automation |
💡 Tip of the Day:
If your Terraform apply depends on a script working perfectly you’ve already lost. Use provisioners for bootstrap, not orchestration.
📚 Resources & References
1️⃣ Provisioners Documentation
🔗 Docs
Official guide with usage, examples, and caveats.
2️⃣ Provisioners vs User Data
🔗 Learn
How to bootstrap EC2 instances the right way.
3️⃣ SSH Connection Block Details
🔗 Docs
Reference for remote-exec connection settings.
4️⃣ Terraform local-exec
🔗 Doc
Use when you want local automation without real infra.
5️⃣ Null Resource
🔗 Doc
🔗Let’s Stay Connected
📱 Join Our WhatsApp Community
Get early access to AI/ML, Cloud & Devops resources, behind-the-scenes updates, and connect with like-minded learners.
➡️ Join the WhatsApp Group
✅ Follow Me for Daily Tech Insights
➡️ LinkedIN
➡️ YouTube
➡️ X (Twitter)
➡️ Website
Conclusion
Terraform provisioners are powerful but that power comes with complexity and fragility.
Use local-exec to trigger lightweight local actions. Use remote-exec sparingly for immediate bootstrapping. Avoid running your business logic inside a provisioner. And when in doubt, reach for user_data, baked images, or external tools instead.
Infrastructure as code works best when it’s declarative, reliable, and idempotent. Provisioners? They’re the exception not the rule.
