Terraform Provisioner Behaviour Explained

Published: 2025-08-30
7 min read
Share:

Terraform Provisioner Behaviour determines when provisioners run and how Terraform reacts when those provisioners succeed or fail. Understanding this behavior helps prevent unexpected deployments, failed automation, and unnecessary resource replacements.

If you've ever wondered why a provisioner executed at a specific stage or why Terraform marked a resource for recreation after a failed script, this guide explains exactly what is happening behind the scenes.

If you're new to provisioners, start with this guide on Terraform Provisioners Guide before diving deeper into execution behavior.

What Is Terraform Provisioner Behaviour?

Provisioners allow Terraform to execute scripts or commands on local or remote systems during infrastructure operations.

Terraform supports different execution behaviors that control:

  • When a provisioner runs
  • Whether it runs during creation or destruction
  • How Terraform responds if the provisioner fails

Understanding these behaviors is important because provisioners operate outside Terraform's normal resource management model.

According to the official Terraform Registry, provisioners should be considered a last resort when no provider-native solution is available.

Creation-Time Provisioners: Run Tasks After Resource Creation

Creation-time provisioners are the default behavior in Terraform.

When Terraform successfully creates a resource, it immediately executes any associated provisioners.

Common use cases include:

  • Installing software packages
  • Updating configuration files
  • Registering systems with external services
  • Executing local automation scripts

Example:

resource "aws_instance" "web" {
  ami           = "ami-12345678"
  instance_type = "t2.micro"

  provisioner "local-exec" {
    command = "echo ${self.public_ip} >> ~/webserver_ips.txt"
  }
}

In this example, Terraform writes the newly created EC2 instance's public IP address to a local file after the resource is created.

If you're provisioning AWS resources, you may also find these guides useful:

Why Creation-Time Provisioners Are the Default

Most post-deployment tasks need infrastructure to exist before they can run.

For example, if an EC2 instance needs NGINX installed, Terraform must first create the instance before executing the installation commands.

This makes creation-time execution the most common provisioner behavior.

Destroy-Time Provisioners: Run Tasks Before Resource Deletion

Terraform can also execute provisioners before destroying a resource.

To enable this behavior, use the when = destroy argument.

Example:

resource "aws_instance" "web" {
  ami           = "ami-12345678"
  instance_type = "t2.micro"

  provisioner "local-exec" {
    when    = destroy
    command = "echo Destroying ${self.public_ip} >> ~/destroy_log.txt"
  }
}

Before Terraform removes the resource, it executes the provisioner.

Destroy-time provisioners are commonly used for:

  • Deregistering servers from monitoring systems
  • Sending shutdown notifications
  • Performing cleanup tasks
  • Archiving logs
  • Backing up important configuration data

This approach helps create more predictable infrastructure teardown workflows.

Real-World Example: Creation-Time vs Destroy-Time Provisioners

Imagine a web server that must be registered with a monitoring platform when it starts and removed from monitoring when it is terminated.

A creation-time provisioner could:

  • Register the server in monitoring
  • Create inventory records
  • Configure external integrations

A destroy-time provisioner could:

  • Remove monitoring entries
  • Delete inventory records
  • Notify downstream systems

This creates a complete lifecycle workflow around the infrastructure resource.

What Happens When a Terraform Provisioner Fails?

Provisioner failures can significantly affect Terraform operations.

By default, Terraform treats provisioner failures as critical errors.

Example:

provisioner "local-exec" {
  command = "echo 'Hello' > /temp/hello.txt"
}

If the command fails because the directory does not exist or permissions are insufficient, Terraform stops the operation and reports an error.

The default behavior is equivalent to:

on_failure = "fail"

Resource Tainting After Failure

When a creation-time provisioner fails, Terraform marks the resource as tainted.

A tainted resource is considered unreliable because Terraform cannot guarantee that all required configuration steps completed successfully.

During the next terraform apply, Terraform attempts to replace the resource.

This behavior surprises many engineers because the infrastructure itself may be healthy even though the provisioner failed.

For a deeper understanding of resource state management, see:

Continue Deployment Even If a Provisioner Fails

Some provisioner tasks are optional.

In those situations, you can instruct Terraform to continue execution even if the provisioner encounters an error.

Example:

provisioner "local-exec" {
  command    = "some-optional-script.sh"
  on_failure = "continue"
}

With this configuration:

  • Terraform continues execution
  • The resource is not tainted
  • The deployment completes successfully

Use this setting carefully.

Ignoring critical failures can create configuration drift and make troubleshooting more difficult later.

Pro Tip

Many teams avoid provisioners for EC2 bootstrapping and use user_data instead.

Because user_data executes during instance initialization, it is often easier to maintain, more predictable, and better aligned with Infrastructure as Code principles than remote-exec or local-exec provisioners.

Common Provisioner Types in Terraform

local-exec Provisioner

The local-exec provisioner runs commands on the machine executing Terraform.

Example uses:

  • Updating local files
  • Triggering CI/CD processes
  • Calling APIs
  • Sending notifications

remote-exec Provisioner

The remote-exec provisioner runs commands directly on the target resource.

Example uses:

  • Installing software
  • Updating operating system packages
  • Configuring services

Although powerful, HashiCorp generally recommends provider-native alternatives whenever possible.

Terraform Provisioner Behaviour Summary

Terraform supports the following execution patterns:

  • Creation-time provisioners run after resource creation.
  • Destroy-time provisioners run before resource deletion.
  • Failed provisioners stop deployment by default.
  • on_failure = "continue" allows execution to proceed after a failure.
  • Failed creation-time provisioners typically taint resources.

Understanding these behaviors helps you predict how Terraform will respond during infrastructure lifecycle operations.

Best Practices for Terraform Provisioners

Provisioners can solve specific automation problems, but they should not be your first choice.

Follow these recommendations:

  • Use provisioners only when provider-native functionality is unavailable.
  • Prefer user_data, cloud-init, startup scripts, or managed configuration tools.
  • Keep provisioner scripts small and focused.
  • Make scripts idempotent whenever possible.
  • Test scripts independently before deployment.
  • Monitor and log provisioner output.
  • Use on_failure = "continue" only for non-critical operations.
  • Review these additional provisioner limitations and considerations before using provisioners in production.

You may also want to review:

Frequently Asked Questions

What is the default behaviour of a Terraform provisioner?

By default, provisioners run after resource creation and stop the Terraform operation if a command fails.

Can I ignore a provisioner failure?

Yes. Set:

on_failure = "continue"

Terraform will continue execution even if the provisioner fails.

What is a destroy-time provisioner?

A destroy-time provisioner runs before a resource is deleted. It is enabled using:

when = destroy

Will a failed provisioner taint the resource?

Yes. By default, failed creation-time provisioners cause Terraform to mark the resource as tainted so it can be replaced during a future deployment.

Generally, no.

HashiCorp recommends using provider-native mechanisms whenever possible. Provisioners should be reserved for situations where no better alternative exists.

Final Thoughts

Terraform Provisioner Behaviour controls when automation runs and how Terraform responds when that automation succeeds or fails.

Understanding creation-time execution, destroy-time execution, resource tainting, and failure handling helps you build more predictable infrastructure workflows.

In most production environments, provider-native features such as cloud-init, startup scripts, and resource-specific configuration options should be preferred. When provisioners are necessary, keeping them simple and well-tested reduces operational risk.

Free Engineering ToolsNEW

8 free, 100% client-side tools for developers — no signup, no data uploads.

Explore all tools