Using Provisioners, local-exec, or remote-exec

Learn the basics of Terraform provisioners and when to use local and remote provisioners.

When a resource is created, we may have some scripts or operations we’d like to perform locally or on the remote resource. Terraform provisioners are used to accomplish this goal. To a certain degree, they break the declarative and idempotent model of Terraform. Provisioner execution isn’t necessarily atomic or idempotent because it’s executing an arbitrary script or instruction. Terraform isn’t able to track the results and status of provisioners in the same way it can for other resources. For this reason, HashiCorp recommends using provisioners only as a last resort.

Use cases

There are three basic provisioner use cases:

  • Loading data into a virtual machine.
  • Bootstrapping a virtual machine for a config manager.
  • Saving data locally on the system.

The remote-exec provisioner allows us to connect to a remote machine via Windows Remote Management (WinRM) or Secure Shell (SSH) and run a script remotely. Note that this requires the machine to accept a remote connection. Instead of using remote-exec to pass data to a virtual machine, we use the tools in our cloud provider of choice to pass data. That could be the user_data argument in AWS or custom_data in Azure. All public clouds support some type of data exchange that doesn’t require remote access to the machine.

In addition to the remote-exec provisioner, there are also provisioners for Chef, Puppet, Salt, and other configuration managers. They allow us to bootstrap the virtual machine to use our config manager of choice. A better alternative is to create a custom image with the config manager software already installed and register it with the config management server at bootup using one of the data loading options mentioned above.

We may wish to run a script locally as part of our Terraform configuration by using the local-exec provisioner. In some cases, there’s a provider that already has the functionality we’re looking for. For instance, the Local provider can interact with files on our local system. Still, there may be situations where a local script is the only option.

Example usage

Here’s an example usage of different provisions.

We can add a provisioner block inside the resource block of a compute instance.

Get hands-on with 1200+ tech skills courses.