Terraform Actionを使ってみた。 命令的な操作をリソースのライフサイクルイベントに紐づけて実行できます
Terraform Actions とは
Terraform v1.14 で追加された新機能です。これまで Terraformは「宣言的」にリソースを管理するだけでしたが、Actions によって「命令的な操作」をライフサイクルイベントに紐付けて実行できるようになりました。
例えば、以下のようなActionブロックを定義します。CloudFrontのキャッシュの全無効化を実施するActionです。
action "aws_cloudfront_create_invalidation" "cache_bust" {
config {
distribution_id = aws_cloudfront_distribution.site.id
paths = ["/*"]
timeout = 900
}
}
これを2つの方法で実行できます。
1. CLIで実行
-invoke オプションで、実行したいActionを明示的に指定します。
% terraform apply -invoke=action.aws_cloudfront_create_invalidation.cache_bust
(略)
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.aws_cloudfront_create_invalidation.cache_bust will be invoked
action "aws_cloudfront_create_invalidation" "cache_bust" {
config {
distribution_id = "E2DE08ODLWUNIV"
paths = [
"/*",
]
timeout = 900
}
}
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.aws_cloudfront_create_invalidation.cache_bust (triggered by CLI)
Action action.aws_cloudfront_create_invalidation.cache_bust (triggered by CLI): Starting cache invalidation for CloudFront distribution E2DE08ODLWUNIV...
Action action.aws_cloudfront_create_invalidation.cache_bust (triggered by CLI): Creating invalidation request for 1 path(s)...
Action action.aws_cloudfront_create_invalidation.cache_bust (triggered by CLI): Invalidation I5HIO1HL8629S7IUZJO43K1C6U created, waiting for completion...
Action action.aws_cloudfront_create_invalidation.cache_bust (triggered by CLI): Invalidation I5HIO1HL8629S7IUZJO43K1C6U is currently 'InProgress', continuing to wait for completion...
Action action.aws_cloudfront_create_invalidation.cache_bust (triggered by CLI): CloudFront cache invalidation I5HIO1HL8629S7IUZJO43K1C6U completed successfully for distribution E2DE08ODLWUNIV
Action complete: action.aws_cloudfront_create_invalidation.cache_bust (triggered by CLI)
Apply complete! Resources: 0 added, 0 changed, 0 destroyed. Actions: 1 invoked.
2. 他リソースのライフサイクルイベントで発火
本命の使い方はこちらです。
CloudFrontのキャッシュを削除したいケースとしては、オリジンのコンテンツを更新したときが代表的です。コンテンツ更新をトリガーに、このキャッシュ削除Actionを自動実行できます。
サンプルとして、S3に静的サイト(site/index.html、site/style.css)を配置し、コンテンツが変更されたときにキャッシュ削除とデプロイ完了通知を自動実行する構成を組んでみます。
まず、デプロイ完了通知のActionは未作成なので作成します。
resource "aws_sns_topic" "notifications" {
name = "${var.project_name}-deploy-notifications"
tags = {
Project = var.project_name
}
}
resource "aws_sns_topic_subscription" "email" {
topic_arn = aws_sns_topic.notifications.arn
protocol = "email"
endpoint = var.notification_email
}
action "aws_sns_publish" "deploy_notify" {
config {
topic_arn = aws_sns_topic.notifications.arn
subject = "[${var.project_name}] デプロイ完了"
message = jsonencode({
project = var.project_name
environment = "production"
cloudfront = aws_cloudfront_distribution.site.domain_name
s3_bucket = aws_s3_bucket.site.id
})
}
}
次にS3で実現する静的サイトの部分です。site/配下のファイルをfileset()で列挙し、for_eachで1オブジェクトずつS3にアップロードします。ファイルの内容が変わるとfilemd5で計算したetagが変わるため、apply時に差分として検知できます。
locals {
content_dir = "${path.module}/site"
content_files = fileset(local.content_dir, "**")
}
resource "aws_s3_object" "site" {
for_each = local.content_files
bucket = aws_s3_bucket.site.id
key = each.value
source = "${local.content_dir}/${each.value}"
etag = filemd5("${local.content_dir}/${each.value}")
}
このコンテンツ変更を検知してActionを発火させるのが、次のterraform_dataリソースです。
resource "terraform_data" "deploy_trigger" {
input = sha256(jsonencode({
for key, obj in aws_s3_object.site : key => obj.etag
}))
lifecycle {
action_trigger {
events = [after_create, after_update]
actions = [
action.aws_cloudfront_create_invalidation.cache_bust,
action.aws_sns_publish.deploy_notify,
]
}
}
depends_on = [
aws_s3_bucket_policy.site,
aws_sns_topic_subscription.email,
]
}
ポイントはinputの作り方です。全オブジェクトのkeyとetagをまとめてsha256でハッシュ化しているため、次のどちらが起きてもinputの値が変わり、terraform_dataが更新対象になります。
- 既存ファイルの内容が変わる(
etagが変わる) - ファイルが追加・削除される(ハッシュ化対象の
keyの集合が変わる)
inputが変化するとterraform_dataはafter_update(初回applyではafter_create)イベントを発火し、action_triggerのactionsに登録した2つのActionが呼び出されます。
action.aws_cloudfront_create_invalidation.cache_bust: CloudFrontのキャッシュを全パス(/*)で無効化action.aws_sns_publish.deploy_notify: SNS経由でデプロイ完了をメール通知
depends_onでaws_s3_bucket_policy.siteとaws_sns_topic_subscription.emailを指定しているのは、バケットポリシーやSNSサブスクリプションが整う前にActionが実行されてしまわないようにするためです。
これで、site/index.htmlの内容を書き換えてapplyするだけで、S3オブジェクトの更新に続けてキャッシュ削除とデプロイ通知が自動的に呼び出されるようになりました。CLIで都度-invokeするのと違い、コンテンツ更新時にキャッシュ削除を忘れる心配がなくなります。
何が嬉しいのか
これまでTerraformで「デプロイ後にCloudFrontのキャッシュを消す」「完了をSNSで通知する」といった操作をしたい場合、local-exec provisionerやCI/CDのTerraform実行後段のスクリプトに頼るのが定番でした。しかし、これらはTerraformのplan/applyのライフサイクル外で動くため、実行されたかどうかの確認や実行順序の管理はTerraformの管轄外になってしまいます。これらをActionsに移行すると嬉しいポイントは次の3つです。
1. plan/applyのフローに乗る
「1. CLIで実行」で見た通り、-invokeで単体実行する際はterraform applyが呼び出すActionを明示し、実行前に承認を求めます。CI/CDスクリプトに埋め込んだaws cloudfront create-invalidationのように、Terraformの外側でひっそり実行されるオペレーションがなくなります。
2. リソースのライフサイクルに直接フックできる
action_triggerのevents(after_create / after_updateなど)でリソースの状態変化に紐付けられるため、「コンテンツを更新したらキャッシュを消す」を毎回手動で実行する必要がありません。
3. 複数の運用操作を1か所にまとめられる
action_triggerのactionsにはActionをリストで複数指定できます。本記事の例のように「キャッシュ削除 → デプロイ通知」のような一連の運用フローをコード側に集約できます。
各 Provider 対応状況 (2026年7月9日時点)
AWS
Terraform AWS Provider(v6.x系)では、以下の11種類のActionが提供されています。
aws_cloudfront_create_invalidationaws_codebuild_start_buildaws_dms_start_replication_task_assessment_runaws_dynamodb_create_backupaws_ec2_stop_instanceaws_events_put_eventsaws_lambda_invokeaws_ses_send_emailaws_sfn_start_executionaws_sns_publishaws_transcribe_start_transcription_job
aws_lambda_invoke は汎用性が高そうですね。
Azure
azurerm providerでは、以下の6種類のActionが提供されています。
azurerm_data_protection_backup_instance_protectazurerm_front_door_cache_purgeazurerm_managed_redis_databases_flushazurerm_mssql_execute_jobazurerm_netapp_volume_bucket_credentialsazurerm_virtual_machine_power
tfe
HCP Terraform / Terraform Enterprise を操作する公式provider tfe では、以下の1actionが提供されています。
未提供provider
以下のproviderに関しては現時点でActionsは提供されていないことを確認しました。
補足
terraform apply -invoke="xxx" 実行時は他のコードは無視される
例えば新リソースをコードに追加したとします。
+ resource "aws_s3_bucket" "new" {
+ }
-invoke なしで terraform apply もしくは terraform plan を実行した場合、当然以下のようにバケットをひとつ新規作成するプランが作成されます。
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:
# aws_s3_bucket.new will be created
+ resource "aws_s3_bucket" "new" {
+ acceleration_status = (known after apply)
+ acl = (known after apply)
+ arn = (known after apply)
+ bucket = (known after apply)
+ bucket_domain_name = (known after apply)
+ bucket_namespace = (known after apply)
+ bucket_prefix = (known after apply)
+ bucket_region = (known after apply)
+ bucket_regional_domain_name = (known after apply)
+ force_destroy = false
+ hosted_zone_id = (known after apply)
+ id = (known after apply)
+ object_lock_enabled = (known after apply)
+ policy = (known after apply)
+ region = "ap-northeast-1"
+ request_payer = (known after apply)
+ tags_all = (known after apply)
+ website_domain = (known after apply)
+ website_endpoint = (known after apply)
+ cors_rule (known after apply)
+ grant (known after apply)
+ lifecycle_rule (known after apply)
+ logging (known after apply)
+ object_lock_configuration (known after apply)
+ replication_configuration (known after apply)
+ server_side_encryption_configuration (known after apply)
+ versioning (known after apply)
+ website (known after apply)
}
Plan: 1 to add, 0 to change, 0 to destroy.
ところが、-invoke を追加するとこのコード↔リソース実体の差分は無視され、指定したActionだけが実行されるプランになります。
% terraform apply -invoke=action.aws_cloudfront_create_invalidation.cache_bust
(略)
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.aws_cloudfront_create_invalidation.cache_bust will be invoked
action "aws_cloudfront_create_invalidation" "cache_bust" {
config {
distribution_id = "E2DE08ODLWUNIV"
paths = [
"/*",
]
timeout = 900
}
}
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:
CLI実行の際に複数のActionを実行できる?
できません。
-invoke を複数個指定したところエラーになりました。
% terraform apply \
-invoke=action.aws_cloudfront_create_invalidation.cache_bust \
-invoke=action.aws_sns_publish.deploy_notify
╷
│ Error: Invalid arguments
│
│ Only one action can be invoked at a time.
╵
For more help on using this command, run:
terraform apply -help
local-execでやる場合との比較
「何が嬉しいのか」欄で少し触れましたが、今回の例の「デプロイ後にCloudFrontのキャッシュを消す」「完了をSNSで通知する」といった操作をしたい場合、これまでもlocal-exec provisionerを使って実現できました。例えば以下のようなコードです。local-exec内でAWS CLIを実行します。
resource "terraform_data" "deploy_trigger" {
triggers_replace = sha256(jsonencode({
for key, obj in aws_s3_object.site : key => obj.etag
}))
provisioner "local-exec" {
command = <<-EOT
aws cloudfront create-invalidation \
--distribution-id ${aws_cloudfront_distribution.site.id} \
--paths "/*"
EOT
}
provisioner "local-exec" {
command = <<-EOT
aws sns publish \
--topic-arn ${aws_sns_topic.notifications.arn} \
--subject "[${var.project_name}] デプロイ完了" \
--message '${jsonencode({
project = var.project_name
environment = "production"
cloudfront = aws_cloudfront_distribution.site.domain_name
s3_bucket = aws_s3_bucket.site.id
})}'
EOT
}
depends_on = [
aws_s3_bucket_policy.site,
aws_sns_topic_subscription.email,
]
}
この local-exec案とAction案を比較してみます。結論から言うとActionsの方が優れていると考えています。
| 観点 | local-exec + AWS CLI | Terraform action |
|---|---|---|
| ローカル依存 | 実行環境に aws CLI・シェルが必要 | 不要(プロバイダーが実行)。CI や HCP でも一貫 |
| 認証情報 | シェルの環境変数に依存(provider と別系統になりがち) | provider の認証をそのまま使う(assume_role 等も一貫) |
| 設定の検証 | 生の文字列コマンド。タイポ・エスケープ事故 | スキーマ検証あり |
| plan での可視性 | ほぼ不透明(apply 時に走るだけ) | plan にアクション実行が現れる |
| 位置づけ | HashiCorp 公式が「代替手段を尽くしてから使うべき」との見解 | この用途のために作られた機能 |
| CLIからの単発実行 | 不可(リソースのライフサイクルに紐づく) | terraform apply -invoke=... で可能 |
「plan での可視性」の詳細
上記 local-execのコードで S3ファイルに変更が発生した際のplanは以下の様になります。terraform_data リソースがreplaceされるのがわかるだけで、 local-exec provisionerで定義されているAWS CLIの処理は見えません。
# terraform_data.deploy_trigger must be replaced
-/+ resource "terraform_data" "deploy_trigger" {
~ id = "f42e2c3d-434d-6983-fa7a-9fec8af87209" -> (known after apply)
~ triggers_replace = "6b5b9313b851580d010b2a7b18c5d04a8c021b42e03c8acb3727eac7aba5d96b" -> "bcc13bcaf69c72b31cfff943731b3ec1e61f5d3e4acfe1fab9b1decb3fbb24bc"
}
一方 Actionの場合は以下の様に実行されるActionの内容もplan内で確認可能です。
# terraform_data.deploy_trigger will be updated in-place
~ resource "terraform_data" "deploy_trigger" {
id = "1aed8433-5931-ba60-b1da-a4109b14bee2"
~ input = "6b5b9313b851580d010b2a7b18c5d04a8c021b42e03c8acb3727eac7aba5d96b" -> "bcc13bcaf69c72b31cfff943731b3ec1e61f5d3e4acfe1fab9b1decb3fbb24bc"
~ output = "6b5b9313b851580d010b2a7b18c5d04a8c021b42e03c8acb3727eac7aba5d96b" -> (known after apply)
}
# Actions to be invoked after this change in order:
action "aws_cloudfront_create_invalidation" "cache_bust" {
config {
distribution_id = "E2DE08ODLWUNIV"
paths = [
"/*",
]
timeout = 900
}
}
action "aws_sns_publish" "deploy_notify" {
config {
message = jsonencode(
{
cloudfront = "d3sq6rvt2o83h0.cloudfront.net"
environment = "production"
project = "actions-test"
s3_bucket = "actions-test-site-047429787746"
}
)
subject = "[actions-test] デプロイ完了"
topic_arn = "arn:aws:sns:ap-northeast-1:047429787746:actions-test-deploy-notifications"
}
}
検証環境情報
- Terraform version: v1.14.0
- Terraform AWS Provider version: 6.42.0
参考情報
- Release v1.14.0 · hashicorp/terraform
- Invoke an action | Terraform | HashiCorp Developer
- action_trigger | resource block reference | Terraform | HashiCorp Developer
- Planning Options | terraform plan command reference | Terraform | HashiCorp Developer
- action_trigger | lifecycle meta-argument reference | Terraform | HashiCorp Developer








