Terraform provisioners local-exec vs remote-exec vs file
There are some situations when deploying infrastructure that Terraform doesn’t handle everything automatically. For example if you need to run a script or copy a file provisioners comes to the story because they let you run commands or transfer files during resource creation time. In this blog post we will break down Terraform provisioners local-exec vs remote-exec vs file, explain differences between them and show some usecase where each is useful.
What Are Terraform Provisioners?
Terraform provisioners let you run scripts or commands during or after the creation of a resource and these actions can happen on both your local machine where Terraform is running or on some remote resource such as AWS EC2 instance for example.
Provisioners are often used for configuration tasks, initial setup or bootstrapping that cannot be done using standard Terraform resource arguments.
Even though Terraform usually forces declarative approach (you define what you want and not how to do it), provisioners provide an imperative approach (a way to run custom things when necessary).
Lets say you need to generate and copy SSH key or some setup config file to remote EC2 instance dynamically during Terraform apply after instance is created. For this purpose you can use file provisioner to upload the file and remote-exec to execute command using it.
This is one simple example:
provisioner "file" {
source = "script.sh"
destination = "/tmp/script.sh"
}
provisioner "remote-exec" {
inline = [
"chmod +x /tmp/script.sh",
"/tmp/script.sh arg1"
]
}Terraform Provisioner local-exec
This runs on your local machine, or we can say the one executing Terraform which is usually either laptop or CI/CD runner. One of the examples could be installing Python dependencies into Lambda Layer folder before packaging it.
resource "terraform_data" "install_lambda_dependencies" {
triggers_replace = [
timestamp()
]
provisioner "local-exec" {
command = "pip3 install -r requirements.txt -t ./lambda_layer/python"
}
}What this does:
- Run pip3 install locally (on your machine or CI runner)
- Installs Python dependencies from requirements.txt file
- Saves the packages into a folder that will be turned into a Lambda layer and used by function in AWS
Why local-exec for this purpose:
This cannot be achieved in Terraform declaratively as Terraform can’t natively run pip or manipulate local files during apply but local-exec gives you a way to do so.
When to use:
- Triggering a local script
- Calling an external API or CLI tool
- Writing output to a local file
- …
Terraform Provisioner remote-exec
This runs on the remote resource, like an EC2 instance after it has been created. The example for remote-exec is available in the beginning of the blog so we can use it to further explain its purpose.
Why this does:
- The setup.sh script is uploaded during apply using the file provisioner
- user_data can’t reference or run this script it doesn’t exist yet when the instance boots
- remote_exec ensures the script is run after it’s uploaded
Why remote-exec for this purpose:
This can’t be achieved declaratively in Terraform because the script is uploaded during the apply phase using the file provisioner and Terraform has no native way to run that script afterward.
Using remote-exec gives you control to execute the script only after it’s been uploaded, making it ideal for tasks that depend on dynamic or runtime content.
When to use remote-exec:
- Running commands inside a provisioned resource (e.g., EC2, VM)
- Executing a script uploaded via file provisioner
- Installing software or applying changes after provisioning
- Restarting services or triggering app setup
- Performing post-deploy tweaks not possible with user_data
- …
When to use file:
- Uploading a script to a remote machine before executing it
- Copying config files, environment files, or secrets to a server
- Transferring dynamic files generated during the Terraform run
- Preparing files needed by remote-exec or other provisioning steps
- …
Conclusion
Terraform provisioners offer a powerful way to handle tasks that go beyond what declarative code can do — like installing packages, copying files, or running scripts during provisioning. While they shouldn’t be your first choice for every situation, they’re extremely useful when you need more control over the setup process.
In this post, Terraform provisioners local-exec vs remote-exec vs file, we explored how each provisioner works, when to use them, and what to avoid. Use them wisely, and they can become a helpful tool in your Terraform workflow.
If this blog saved you time, support me with a coffee!
Thanks to everyone who’s supported!







