Deep Dive into Terraform Provisioners: Local-Exec, Remote-Exec, and Null Resources Explained

Chethana S VChethana S V
4 min read

Provisioners in Terraform give you a handy way to run scripts or commands when you’re setting up infrastructure. Whether you want to configure something, install software, or run custom scripts after creating a resource, provisioners have got you covered.

In this blog, we’ll walk through some of the most commonly used ones—local-exec, remote-exec, and null resource—with examples and tips to get the best out of them.

1. Local Exec Provisioner

The local-exec provisioner runs commands locally on the machine where Terraform is executed. This is useful for tasks that require actions on the local system, such as triggering scripts or notifying external systems.

Syntax Example

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

  provisioner "local-exec" {
    command = "echo 'Instance ${self.id} has been created'"
  }
}

In this example, after the AWS EC2 instance is created, Terraform runs the echo command locally, displaying the instance ID.

Use Cases

  • Triggering post-deployment scripts.

  • Sending notifications (e.g., via Slack or email).

  • Running CLI tools or local scripts.

Best Practices

  • Ensure the command is idempotent (safe to run multiple times).

  • Use local-exec only when actions must be performed on the local machine.

2. Remote Exec Provisioner

The remote-exec provisioner runs commands on a remote resource (e.g., a virtual machine) after it has been created. This is typically used for tasks like software installation and configuration.

How It Works

  • Terraform connects to the remote resource using SSH (for Linux) or WinRM (for Windows).

  • It executes one or more specified commands on the remote machine.

Syntax Example

resource "aws_instance" "example" {
  ami           = "ami-12345678"
  instance_type = "t2.micro"
  key_name      = "my-key"

  provisioner "remote-exec" {
    connection {
      type        = "ssh"
      user        = "ec2-user"
      private_key = file("~/.ssh/my-key.pem")
      host        = self.public_ip
    }

    inline = [
      "sudo apt update",
      "sudo apt install -y nginx"
    ]
  }
}

Use Cases

  • Installing necessary software (e.g., web servers, databases).

  • Running initialization scripts on remote machines.

  • Bootstrapping configuration management tools like Ansible or Chef.

Best Practices

  • Ensure the commands are idempotent.

  • Use remote-exec only when necessary; for large-scale configuration, consider using configuration management tools.

3. Null Resource with Provisioners

A null resource in Terraform is a placeholder resource that doesn’t manage any actual infrastructure. It is often used to run provisioners independently or to orchestrate tasks that don’t fit naturally with a specific infrastructure resource.

Syntax Example

resource "null_resource" "example" {
  provisioner "local-exec" {
    command = "echo 'Running null resource task'"
  }

  triggers = {
    build_number = var.build_number
  }
}

In this example, a null resource is used to execute a local command. The triggers argument ensures that the null resource is re-created when the value of var.build_number changes.

Use Cases

  • Running scripts or tasks that don’t directly relate to a Terraform-managed resource.

  • Automating post-deployment tasks (e.g., updating DNS, sending notifications).

  • Orchestrating complex workflows by enforcing dependencies using depends_on.

Best Practices

  • Use the triggers argument to ensure that tasks are only executed when necessary.

  • Combine null resources with provisioners for tasks that need to be decoupled from specific infrastructure resources.

Key Differences Between Local Exec, Remote Exec, and Null Resources

Provisioner TypeExecution LocationPrimary Use Case
Local ExecLocal machine (where Terraform runs)Running local scripts, notifying external systems
Remote ExecRemote machine (via SSH/WinRM)Configuring remote servers, installing software
Null ResourceLocal or RemoteOrchestrating independent tasks or workflows

When to Avoid Using Provisioners

Although provisioners are powerful, they should be used with caution. Here are scenarios where they should be avoided:

  1. Idempotency Concerns: Provisioners can introduce non-idempotent behavior, meaning running Terraform multiple times may not produce consistent results.

  2. Long-Term Configuration Management: For complex or long-term server configuration, it’s better to use dedicated configuration management tools like Ansible, Chef, or Puppet.

  3. Over-Reliance on Local Scripts: Relying too heavily on local-exec provisioners can make your Terraform configuration less portable.

Conclusion

Provisioners in Terraform are a useful feature for executing scripts or commands during the resource lifecycle. While local-exec and remote-exec provisioners are ideal for running local and remote commands respectively, null resources provide flexibility in orchestrating independent tasks. By following best practices and understanding when to avoid provisioners, you can effectively integrate them into your Terraform workflows.

If you found this guide helpful, feel free to share it with others or leave your thoughts in the comments!

0
Subscribe to my newsletter

Read articles from Chethana S V directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Chethana S V
Chethana S V

I’m a DevOps engineer passionate about automation, cloud infrastructure, and security. Currently, I’m diving into the world of DevSecOps, integrating security into the DevOps lifecycle. Always eager to learn and apply new technologies, I aim to build secure, scalable systems and share my experiences along the way through this blog.