DEV Community

Puneet Gavri
Puneet Gavri

Posted on

TERRAFORM ACTIONS : The HashiConf '25 Drop We’ve All Been Waiting For!!!!

terraform actions
At HashiConf this week, HashiCorp rolled out something many of us DevOps folks have been wishfully dreaming about: Terraform Actions.

If you've worked with Terraform for longer than five minutes, you know the philosophy: immutable infra only! Apply, destroy, and between those two, you're not really supposed to do anything.

But let's admit it, we've all done stuff. Executed a script on a VM, initiated a migration, or invoked something without modifying the .tf file. Most often, the "solution" was provisioners or some other workarounds, which, if we're being honest, always seemed like duct-taping your car bumper. HashiCorp docs even do not recommend them saying: "try not to use these."

So what's new?

Terraform Actions is now a first-class block within HCL that allows you to define what should occur between the creation and teardown of infrastructure.

It's as if HashiCorp is declaring:

"Fine, we know you're going to do this anyway. Here's a tidy, supported means of doing it."

Some actual applications:

  • Run DB migrations post-deployment of a new instance.
  • Rotate secrets without taking everything down.
  • Make config tweaks safely without violating immutability guarantees.
  • Invoke a Lambda, stop an AWS EC2 instance, invalidate an AWS CloudFront cache, etc.

Terraform v1.14.0-beta2 (pre-release) just dropped yesterday (Sept 25, 2025). As of today (the very next day), in the AWS provider I only see three Actions available so far aws_lambda_invoke, aws_cloudfront_create_invalidation, and aws_ec2_stop_instance.

To really see the difference between the old way and the new way, let’s take a simple example: say we need to invoke a Lambda function (yep, in AWS).

Before (old school way!):

If any existing Lambda is to be invoked, we could use a data block:

data "aws_lambda_invocation" "call_hellolambda" {
function_name = "hellolambda"
input = jsonencode({ })
}

output "lambda_response" {
value = data.aws_lambda_invocation.call_hellolambda.result
}
Enter fullscreen mode Exit fullscreen mode

Sure, this works — but it runs the Lambda every single time you do terraform apply. Not exactly flexible.

OR

If we wish to invoke the lambda just after creation, we can use a resource block:

resource "aws_lambda_function" "example" {
function_name = "hellolambda"
}

resource "aws_lambda_invocation" "invoke_after_creation" {
function_name = aws_lambda_function.example.function_name
depends_on = [aws_lambda_function.example]

input = jsonencode({
})
}
Enter fullscreen mode Exit fullscreen mode

Here the Lambda gets invoked right after creation. Sounds good… until you realize it’s now stuck to the lifecycle. If nothing changes in the resource config, Terraform won’t re-invoke it.

OR

We were using clunky, duct-taping provisioners:

provisioner "remote-exec" {
inline = ["aws lambda invoke --function-name hellolambda /tmp/lambda_output.txt"]
connection { ... }
}
Enter fullscreen mode Exit fullscreen mode

This is the duct-tape-and-prayers method. It’ll run… until the day it doesn’t, and then you’re the lucky one ssh’ing into a box at 2 AM.

After Terraform Actions (the new way of On-Demand Invocations!!)

You can configure (the new) action blocks in your Terraform configuration that are not referenced anywhere else in your code like below:

action "aws_lambda_invoke" "test" {
config {
function_name = "hellolambda"
payload = jsonencode({})
}
}
Enter fullscreen mode Exit fullscreen mode

This standalone action can be triggered using the Terraform CLI. To invoke them, you use the -invoke=<action address> flag with the terraform plan or terraform apply commands:

terraform apply -auto-approve -invoke=action.aws_lambda_invoke.test

Enter fullscreen mode Exit fullscreen mode

You would see output like this:

cli output

The above example is just for ad-hoc action which you can trigger using the CLI. You can also bind actions to the lifecycle of a resource.

Why Terraform Actions Feel Like an Upgrade

The good thing about Terraform Actions is that they finally give us a clean, native way to run operations in Terraform without resorting to hacks. A few reasons this matters:

  • You can perform on-demand tasks like invoking a Lambda function, rotating a secret, stopping/starting an EC2, without tying it to the whole “create/destroy” lifecycle.
  • No more duct-taping SSH commands or local-exec scripts just to run a one-off task.
  • Actions are visible and auditable in your config, instead of tasks/operations hiding in random shell scripts.
  • They fit naturally into CI/CD pipelines hence safer, easier, and a lot less fragile than the workarounds most of us have been using.

From the release notes:

Actions are provider-defined and meant to codify use cases outside the normal CRUD model in your Terraform configuration. Providers can define Actions like aws_lambda_invoke or aws_cloudfront_create_invalidation that do something imperative outside of Terraform’s normal CRUD model. You can configure such a side-effect with an action block and have actions triggered through the lifecycle of a resource or through passing the -invoke CLI flag.

Conclusion

Terraform Actions are still in beta and starting with just a handful of examples, but honestly, it already feels like a fresh breeze after years of duct-taping solutions.

It surely will ease our pain of playing with various workarounds. This was my initial reaction article on this wonderful addition by HashiCorp.

Excited to explore other new features and enhancements in this latest version 1.14 and cannot wait to drop more such content in "The Devops Drop".

Subscribe to The Devops Drop on LinkedIn : https://www.linkedin.com/build-relation/newsletter-follow?entityUrn=7375749694414782466

Top comments (0)