👋 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

local-exec

Runs a command on your local machine

remote-exec

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, aws CLI

  • Notifying 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-init or Packer

  • Anything you can do with native Terraform resources

  • Provisioning infrastructure in CI/CD pipelines

🔁 Safer Alternatives

Use Case

Better Option

VM bootstrapping

Use user_data in EC2

Config management

Use Ansible/Chef/Puppet

File deployment

Use baked AMIs or cloud-init

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.

Keep Reading

No posts found