WintelGuy.com

Terraform Actions: What They Are and How They Work

Contents

Introduction to Terraform Actions

Terraform is a declarative tool: you describe the desired end state of your infrastructure, and Terraform figures out how to reach it. Most of the time, this model works extremely well. Resources are created, updated, or destroyed based on configuration changes, and Terraform tracks those changes through state.

However, not all infrastructure-related tasks fit neatly into a resource lifecycle. Common examples include:

  • Running a one-time operational command
  • Integrating with an external system
  • Performing an administrative API call
  • Executing setup steps or validation logic

Traditionally, these activities were performed using provisioners. While provisioners made such tasks possible, they also introduced ambiguity around lifecycle, ordering, error handling, and idempotency. Over time, their limitations became clear, and Terraform began discouraging their use except as a last resort.

Terraform actions address this problem directly. Actions provide an explicit mechanism for executing imperative operations within a Terraform workflow. Unlike resources, actions are not managed over time, do not represent persistent infrastructure objects, and are not reconciled against state. Instead, they invoke a specific operation at a specific point in the deployment process.

Actions are:

  • Provider-defined and provider-aware
  • Explicitly invoked via the Terraform CLI or during terraform apply
  • Integrated into Terraform's dependency graph
  • Executed in a well-defined order with predictable failure handling

This makes them particularly useful for tasks that must happen alongside infrastructure changes, such as post-deployment configuration steps, administrative operations, or one-off API calls.

By introducing actions, Terraform offers a cleaner, safer, and more expressive alternative to provisioners, while preserving the declarative approach that makes Terraform reliable and predictable.

In the sections that follow, we'll explore how actions are defined, how they are executed, and how different providers support them.

Back to Top

The Action Block

Terraform actions are defined using an action block, which declares an imperative operation that Terraform should execute when the action is invoked. Unlike resources or data sources, an action does not represent a managed object. Instead, it represents an explicit operation that is intentionally run at a specific point in the configuration's deployment process.

At a high level, an action block answers three questions:

  • Which provider implements the action?
  • Which action type should be executed?
  • What inputs are required to perform the action?

An action block follows a provider-defined structure, but the general format is consistent:

action "<TYPE>" "<LABEL>" { config { # Defines an action <PROVIDER-SPECIFIC ARGUMENTS> } provider = <PROVIDER.ALIAS> # Provider reference }

The config block may contain arguments, nested blocks, or a combination of both. Its structure is entirely provider-specific and defines how the action behaves when it is executed.

Similarly to other resources, action blocks also support the count and for_each meta-arguments, allowing multiple action instances to be declared by a single configuration block when needed.

Elsewhere in the configuration, an action can be referenced using the action.<TYPE>.<LABEL> syntax. This allows actions to participate in dependency relationships with other resources.

Because actions are implemented by providers, they benefit from the same provider-level capabilities as other Terraform constructs, including:

  • Native authentication and authorization
  • Consistent error reporting
  • Provider-controlled versioning

The action block also has several important properties that clearly distinguish it from other Terraform blocks:

  • No lifecycle - Actions are not created, updated, or destroyed. They represent executable operations.
  • No state reconciliation - Terraform does not track or reconcile actions in state. If an action is invoked, it runs.
  • Explicit execution - Defining an action block alone does not cause it to execute. Actions must be explicitly invoked, either through the CLI or via supported action-triggering mechanisms.

In the following section, we'll look more closely at how actions are invoked, when they run during a Terraform workflow, and how execution success or failure affects an apply.

Back to Top

Action Invocation

Defining an action block alone does not cause it to execute. Terraform actions are explicitly invoked, either directly through the Terraform CLI or indirectly as part of the standard terraform apply workflow. This design reinforces Terraform's declarative model by ensuring that imperative steps run only when intentionally triggered and under well-defined conditions.

Terraform currently supports two invocation models:

  • Direct invocation via the Terraform CLI
  • Automatic invocation through action triggers during terraform apply

Each model serves different use cases and provides varying levels of control over when and how actions are executed.

Back to Top

Direct Invocation from the Terraform CLI

Actions can be executed explicitly using the Terraform CLI, independent of a full terraform apply run. This is useful for operational tasks, ad-hoc workflows, and situations where rerunning an action is needed without modifying infrastructure.

Let's use this code example defining a local_command action to illustrate CLI invocation:

action "local_command" "standalone" { config { command = "bash" arguments = ["-c", "cat -"] stdin = "Standalone Action: Test Message" } }

A typical invocation follows this pattern:

terraform apply -invoke=action.<TYPE>.<LABEL>

This command executes the specified action without planning or applying infrastructure changes. For example:

$ terraform apply -invoke=action.local_command.standalone Terraform will perform the following actions: Plan: 0 to add, 0 to change, 0 to destroy. Actions: 1 to invoke. Terraform will invoke the following action(s): # action.local_command.standalone will be invoked action "local_command" "standalone" { config { arguments = [ "-c", "cat -", ] command = "bash" stdin = "Standalone Action: Test Message" } } Would you like to invoke the specified actions? Terraform will invoke the actions described above, and any changes will be written to the state without modifying real infrastructure There is no undo. Only 'yes' will be accepted to confirm. Enter a value: yes Action started: action.local_command.standalone (triggered by CLI) Action action.local_command.standalone (triggered by CLI): Standalone Action: Test Message Action complete: action.local_command.standalone (triggered by CLI) Apply complete! Resources: 0 added, 0 changed, 0 destroyed. Actions: 1 invoked.

Direct invocation allows operators to:

  • Trigger one-off administrative tasks
  • Rerun failed actions
  • Debug and validate workflows
  • Execute operational commands without affecting managed resources

Back to Top

Invocation Through action_trigger

In many cases, actions must run automatically as part of infrastructure changes. Terraform supports this pattern through action triggers, which define conditions under which an action should execute during terraform apply.

Conceptually, triggers define when an action should run, while the action block defines what should run.

Action triggers reside in the lifecycle block. The action_trigger block specifies a set of arguments that determine which events trigger one or more provider actions, under which conditions the action runs, and which actions Terraform invokes:

resource "<TYPE>" "<LABEL>" { # ... lifecycle { # ... action_trigger { events = [ <EVENT> ] # One or more lifecycle events condition = <EXPRESSION> # Conditional expression (optional) actions = [ action.<TYPE>.<LABEL> ] # List of action references } } }

The action_trigger rule is a block that supports the following arguments:

  • events - a list of lifecycle events to invoke the action; may include one or more of the following events: before_create, after_create, before_update, after_update
  • condition - an optional expression that evaluate to true to invoke the action
  • actions - an ordered list of actions to run when the events and condition arguments are met.

More than one action_trigger rule can be included in a lifecycle block.

During the apply phase, Terraform evaluates all action_trigger blocks and invokes the actions whose trigger conditions are satisfied. If an action fails, Terraform treats the failure as an apply failure and halts further execution.

Importantly, because actions do not represent managed state, they are not replayed automatically unless their trigger conditions are met again or they are explicitly invoked.

action_trigger Example:

# main.tf variable "run_action" { type = bool default = true description = "Set to 'true' to run actions" } variable "input_data" { type = string default = "test" } resource "terraform_data" "trigger" { input = var.input_data lifecycle { action_trigger { events = [after_create, after_update] condition = var.run_action == true # Must evaluate to true to invoke the action actions = [action.local_command.triggered] } } } action "local_command" "triggered" { config { command = "bash" arguments = ["-c", "cat -"] stdin = "Triggered Action: Test Message" } }
$ terraform apply Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: # terraform_data.trigger will be created + resource "terraform_data" "trigger" { + id = (known after apply) + input = "test" + output = (known after apply) } # Actions to be invoked after this change in order: action "local_command" "triggered" { config { arguments = [ "-c", "cat -", ] command = "bash" stdin = "Triggered Action: Test Message" } } Plan: 1 to add, 0 to change, 0 to destroy. Actions: 1 to invoke. Do you want to perform these actions? Terraform will perform the actions described above. Only 'yes' will be accepted to approve. Enter a value: yes terraform_data.trigger: Creating... Action started: action.local_command.triggered (triggered by terraform_data.trigger) terraform_data.trigger: Creation complete after 0s [id=cad0efbd-6a9b-e3d6-f4ae-94702c76da8f] Action action.local_command.triggered (triggered by terraform_data.trigger): Triggered Action: Test Message Action complete: action.local_command.triggered (triggered by terraform_data.trigger) Apply complete! Resources: 1 added, 0 changed, 0 destroyed. Actions: 1 invoked.

The action triggers enable workflows such as:

  • Running a validation step after provisioning
  • Performing post-deployment configuration
  • Executing cleanup or migration steps when resources change

Back to Top

Provider Support Overview

Terraform actions are provider-defined, which means both their availability and behavior depend entirely on the provider implementing them. Unlike core Terraform constructs such as resources or data sources, actions are not universally supported or standardized across providers. As a result, understanding provider-specific capabilities is essential when designing action-based workflows.

The sections below provide a high-level overview of action support across commonly used providers. The exact action types, inputs, and behavior may vary by provider version and should always be verified against provider documentation.

Local Provider

The Local provider from Hashicorp is used to execute commands and manage local resources, such as files. It supports:

  • local_command action which invokes an executable on the local machine.

Back to Top

AWS Provider

The AWS provider exposes actions for operational and administrative tasks that interact directly with AWS APIs but are not modeled as persistent resources.

Here's a list of the actions currently supported by the AWS Terraform provider:

  • aws_cloudfront_create_invalidation - Invalidates objects in CloudFront distribution cache. This action creates an invalidation request and waits for it to complete. Useful for ensuring fresh content is served after updates.
  • aws_codebuild_start_build - Starts a new build in AWS CodeBuild. This action will initiate a build and wait for it to complete, providing progress updates during execution. Commonly used to trigger CI/CD pipelines or custom build workflows.
  • aws_ec2_stop_instance - Stops an EC2 instance. This action will gracefully stop the instance and wait for it to reach the stopped state. Useful for operational automation, maintenance windows, or cost optimization.
  • aws_events_put_events - Sends custom events to Amazon EventBridge so that they can be matched to rules. This action provides an imperative way to emit events from Terraform plans (e.g., deployment notifications) while still allowing Terraform to manage when the emission occurs through action_trigger lifecycle events.
  • aws_lambda_invoke - Invokes an AWS Lambda function with a specified payload. This action allows for imperative invocation of Lambda functions with full control over invocation parameters. Often used for operational hooks or serverless automation.
  • aws_ses_send_email - Sends an email using Amazon SES. This action allows for imperative email sending with full control over recipients, content, and formatting. Useful for notifications or alerts triggered by Terraform workflows.
  • aws_sfn_start_execution - Starts an execution of a Step Functions state machine, enabling orchestration of complex workflows.
  • aws_sns_publish - Publishes a message to an Amazon SNS topic for notifications, fan-out messaging, or event-driven integrations.

Back to Top

Azure Provider

Azure support for actions is currently limited, and the only available action type listed in the Terraform Registry is for virtual machine power management:

  • azurerm_virtual_machine_power - Controls the power state of a virtual machine in Azure. You can power off, power on, or restart a VM as part of an action, enabling operational steps such as cost optimization or maintenance workflows.

Back to Top

Summary and Key Takeaways

Terraform actions provide an explicit way to execute imperative operations alongside declarative infrastructure management. They address long-standing gaps that were previously filled by provisioners, while preserving Terraform's core principles of clarity, predictability, and dependency-aware execution.

Throughout this article, we explored how actions:

  • Represent intentional execution steps, not managed infrastructure
  • Are defined using provider-specific action blocks
  • Can be invoked directly via the Terraform CLI or automatically during terraform apply
  • Integrate cleanly into Terraform's dependency graph
  • Vary in availability and behavior depending on provider support

Key points to keep in mind:

  • Actions are executable operations, not resources - They do not have a lifecycle, are not reconciled in state, and should not be used to model persistent infrastructure.
  • Invocation is always explicit - Actions run only when directly invoked or when their trigger conditions are satisfied during apply.
  • Provider support matters - Action availability, semantics, and guarantees differ across providers. Always consult provider documentation before relying on an action in production.
  • Actions are the preferred alternative to provisioners - They make imperative logic visible, predictable, and better integrated into Terraform workflows.
  • Use actions intentionally and sparingly - Prefer native resources whenever possible, and reserve actions for operational, one-off, or lifecycle-adjacent tasks that do not belong in resource definitions.

Used correctly, Terraform actions help bridge the gap between declarative infrastructure and real-world operational needs, without sacrificing readability, safety, or control.

Back to Top