Skip to main content
In this lesson we move from remote provisioners to local provisioners in Terraform and show how to use the local-exec provisioner to run commands on the machine executing Terraform (your laptop, a jump host, or a CI/CD agent). Unlike remote provisioners, which log into and configure created resources (via SSH, WinRM, etc.), the local-exec provisioner executes a command locally after Terraform completes the resource creation lifecycle event. It does not connect to or modify the created resource; it’s intended for orchestration and automation side-effects.
The image is an illustration explaining local provisioners with a computer screen icon and text pointing out that it happens after the resource is created and doesn't connect to the resource.
Key characteristics
  • Runs on the Terraform host (not on the provisioned resource).
  • Executes after the resource creation or before destruction depending on configuration.
  • Useful for orchestration tasks (writing local files, triggering pipelines, sending notifications).
  • Not suitable for resource configuration or long-running configuration management.
Use cases Local provisioners are commonly used for lightweight automation tasks such as:
  • Writing resource outputs to a local file (for example, capturing a public IP address or a connection string).
  • Triggering shell scripts or CI/CD tasks (invoking additional deployment stages, notifying systems, or kicking off pipelines).
  • Sending alerts or webhooks (Slack notifications, integrations with monitoring/operational tooling).
These are orchestration or automation side effects, not infrastructure configuration tasks.
The image lists three use cases: writing outputs to a file, triggering shell scripts or CI/CD tasks, and sending Slack/webhook alerts.
Use cases at a glance Example: save a storage account connection string locally The following compact example creates an Azure resource group and a storage account, then uses a local-exec provisioner to write the storage account’s primary connection string to a local file named connection_string.txt. Notes applied:
  • Use provider "azurerm" { features {} } (required by recent azurerm provider versions).
  • Wrap provider-marked sensitive values with nonsensitive() when you intentionally need to emit them.
  • Wrap the connection string in single quotes in the shell echo to avoid shell interpretation of semicolons.
Why this works
  • In a provisioner, self refers to the current resource instance (here, the storage account).
  • primary_connection_string is the attribute exposed by the storage account resource that contains the connection string.
  • nonsensitive() unwraps values marked sensitive by the provider so Terraform can emit them. Use this only when you intentionally want the value exposed.
  • Single quotes around the interpolated value prevent semicolons in the connection string from being interpreted by the shell.
Initialize and apply
  1. Initialize the working directory (downloads providers and sets up the environment):
Example initialization output (trimmed):
  1. Apply the configuration (use --auto-approve to skip the confirmation prompt):
Example apply output (truncated):
After apply, the local-exec provisioner runs on the Terraform host and writes the file. Verify it:
Example output:
This demonstrates capturing a provider attribute and storing it locally using a local provisioner.
Local provisioners are best for orchestration or integration steps (e.g., saving outputs locally, invoking CI/CD pipelines, or sending notifications). They are not a substitute for configuration management on created resources.
Avoid using provisioners for critical configuration or secrets handling. Sensitive values written locally may be stored in plaintext unless handled carefully; using nonsensitive() forces Terraform to expose values that would otherwise be masked—use with caution.
Considerations before using provisioners
  • Execution environment: Provisioners run on the machine executing Terraform; ensure that host has required permissions, network access, and installed tooling.
  • Idempotence: Provisioner commands may have side effects and can make terraform apply non-idempotent if not written carefully.
  • Lifecycle coupling: Provisioners are tied to the resource lifecycle (they run during create/destroy as configured). Plan for how retries and re-creates affect side effects.
  • Prefer native solutions: When possible, use built-in provider features or established configuration management/orchestration tools such as Ansible or cloud-init. Use provisioners only when no native alternative exists.
Further reading and references That’s how the local-exec provisioner works and how you can use it to capture and persist resource outputs on the Terraform host.

Watch Video