0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Terraform actionsを試してみる。

Posted at

はじめに

先日ニュースサイトを見ていたところ、Terraform actionsといった機能がHashiCorpから発表されたとの記事を見つけました。

名前からTerraformとGitHub Actionsか何かを組み合わせてCI的なことをするものかとも思いましたが、どうやらTerraformでLambdaやStep Functions等のリソースを実行したりできる機能のようです。

執筆時の2025年10月時点でTerraform actionsはパブリックベータとのことなのでまだこれからのサービスですが、今後のために試してみようと思います。

Terraform actionsとは

今までのTerraformは、各種リソースを作成・管理することを目的としているため、Terraformで作成・管理しているLambdaやStep Functions等のリソースを実行するようなことは、local-execremote-execで、他のシェルやコマンド等を呼び出して実行することしかできませんでした。

しかし、Terraform Version 1.14から、actionブロックが実装されたことで、トリガー設定に従って、LambdaやStep Functionsを実行するようなことができるようになりました。

使用するためには、Terraformとプロバイダ両方の対応が必要となりますが、対応しているプロバイダ・バージョンであれば、リソースの作成からリソースの実行までTerraformだけで完結することも可能です。

数としてはまだ少ないですが、執筆時最新となるAWSプロバイダのVersion6.16.0では以下のアクションブロックが使えるようになっているようです。

アクションブロック名 内容
aws_cloudfront_create_invalidation CloudFrontキャッシュ無効化
aws_codebuild_start_build CodeBuildの実行
aws_ec2_stop_instance EC2の停止
aws_events_put_events EventBridgeへのカスタムイベント送信
aws_lambda_invoke Lambdaの実行
aws_ses_send_email メール送信
aws_sfn_start_execution Step Functionsの実行
aws_sns_publish SNSトピックへのメッセージ発行
aws_transcribe_start_transcription_job Amazon Transcribeの実行

以下、今回参考とさせて頂いたサイト、ドキュメントとなります。

Terraform actionsの使い方

上述の通り、Terraform&プロバイダバージョンを対応しているバージョンまで引き上げる必要がありますが、対応済みであれば、作成や更新が行われた際にTerraform actionsを実行したいリソースに対して、以下のようなlifecycle設定を付与することで、actionsで指定したアクションを実行することができます。

resource "xxx" "yyy" {
(略)

  lifecycle {
    action_trigger {
      events  = [before_create, after_create, before_update, after_update]
      actions = [action.aws_sfn_start_execution.example]
    }
  }
}

eventsは以下のような種類があり、actionsで指定した処理を実行するタイミングを指定できます。

events種類 内容
before_create Terraformがリソースを作成する前にactionsの処理を実行する
after_create Terraformがリソースを作成した後にactionsの処理を実行する
before_update Terraformがリソースを更新する前にactionsの処理を実行する
after_update Terraformがリソースを更新した後にactionsの処理を実行する

なお、before/after_updateが動作するのはupdate in-placeの場合であり、リソースがすでに存在していたとしてもforces replacementの場合は動作しないため、その場合はbefore/after_createで指定する必要があるようですのでご注意ください。

Terraform actionsを実行するための環境の構築

Terraform actionsを試すための環境を構築して実際にTerraformを流してみようと思います。

とりあえず試すのが目的のため、ユースケースなどは無視して、サッと簡単に以下のようなコードを書いてみました。

1ファイルにまとめているので、terraform applyしてAWS環境にデプロイしておいてください。

terraform_actions_test.tf
#----------------------------------------------------------------------------------------------------------------------
# Terraform基本設定
#----------------------------------------------------------------------------------------------------------------------
provider "aws" {
  region = "ap-northeast-1"
}

terraform {
  required_version = ">= 1.14.0"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 6.15.0"
    }
  }
}

#----------------------------------------------------------------------------------------------------------------------
# Data Resource設定
#----------------------------------------------------------------------------------------------------------------------
data "aws_caller_identity" "current" {}
data "aws_region" "current" {}

#----------------------------------------------------------------------------------------------------------------------
# IAM設定
#----------------------------------------------------------------------------------------------------------------------
# Assume Role
data "aws_iam_policy_document" "sfn_assume_role_policy" {
  statement {
    effect  = "Allow"
    actions = ["sts:AssumeRole"]

    principals {
      type        = "Service"
      identifiers = ["states.amazonaws.com"]
    }
  }
}

# IAM Role
resource "aws_iam_role" "sfn_iam_role" {
  name               = "terraform-actions-test-sfn-role"
  assume_role_policy = data.aws_iam_policy_document.sfn_assume_role_policy.json
}

# Policy Attachment
resource "aws_iam_role_policy_attachment" "sfn_policy_attachment" {
  role       = aws_iam_role.sfn_iam_role.name
  policy_arn = "arn:aws:iam::aws:policy/ReadOnlyAccess"
}

#----------------------------------------------------------------------------------------------------------------------
# IAM User設定
#----------------------------------------------------------------------------------------------------------------------
# Terraform actionsテスト用ユーザー
resource "aws_iam_user" "test_user" {
  lifecycle {
    action_trigger {
      events  = [before_update, after_update]
      actions = [action.aws_sfn_start_execution.test]
    }
  }

  name = "terraform-actions-test-user"
  path = "/"

  tags = {
    Environment = "production"
  }
}

#----------------------------------------------------------------------------------------------------------------------
# Step Functions State Machine
#----------------------------------------------------------------------------------------------------------------------
# Step Functions State Machine
resource "aws_sfn_state_machine" "test" {
  name     = "terraform-actions-test"
  role_arn = aws_iam_role.sfn_iam_role.arn

  definition = jsonencode({
    Comment = "Terraform actionsテスト"
    StartAt = "ListUserTags"
    States = {
      ListUserTags = {
        Type = "Task"
        Parameters = {
          UserName = aws_iam_user.test_user.name
        }
        Resource = "arn:aws:states:::aws-sdk:iam:listUserTags"
        End      = true
      }
    }
  })
}

#----------------------------------------------------------------------------------------------------------------------
# 特定ファイルの追跡
#----------------------------------------------------------------------------------------------------------------------
# Terraform actionsテスト用ファイル
resource "terraform_data" "testfile" {
  input = filesha256("./testfile")

  lifecycle {
    action_trigger {
      events  = [before_update]
      actions = [action.aws_sfn_start_execution.test]
    }
  }
}

#----------------------------------------------------------------------------------------------------------------------
# Terraform Actions
#----------------------------------------------------------------------------------------------------------------------
# Terraform actionsリソース
action "aws_sfn_start_execution" "test" {
  config {
    state_machine_arn = "arn:aws:states:${data.aws_region.current.id}:${data.aws_caller_identity.current.account_id}:stateMachine:terraform-actions-test"
    input = jsonencode({
      user_id = "12345"
      action  = "process"
    })
  }
}

なお、actionブロックで他のリソースを直接参照させたり、データリソースで参照させたりすると、更新時に以下のような循環参照のエラーとなってしまったので、Step FunctionsのARNを無理やり作成して参照させています。

循環参照エラー

 Error: Cycle: action.aws_sfn_start_execution.test (instance), 
  aws_iam_user.test_user, aws_sfn_state_machine.test (expand),
  action.aws_sfn_start_execution.test (expand)



作成した環境を使って、以下2点について試してみようと思います。

IAMユーザが更新された場合にStep Functionsを実行

まずはIAMユーザの設定がTerraformによって更新された前後でStep Functionsを実行してみようと思います。

上記のコードより、aws_iam_userリソースにlifecycle設定で以下のように記載されているため、更新前後でStep Functionsのactionsが実行されるようにトリガーされています。

lifecycle設定抜粋
  lifecycle {
    action_trigger {
      events  = [before_update, after_update]
      actions = [action.aws_sfn_start_execution.test]
    }
  }

実際に実行される定義は以下のactionブロックで定義している箇所となり、ここで指定した内容に従って指定したStep Functionsが実行されるようになっています。

Terraform actionsによるStep Functionsの実行設定
action "aws_sfn_start_execution" "test" {
  config {
    state_machine_arn = aws_sfn_state_machine.test.arn
    input = jsonencode({
      user_id = "12345"
      action  = "process"
    })
  }
}

設定更新時に実行されるか確認するため、aws_iam_userリソースに設定しているタグを変更して、applyしてみます。

タグの修正
  tags = {
    Environment = "production"  "develop"に変更
  }

ちなみにタグを変更後にterraform planを実行した結果は以下となりました。

タグ変更後のplan結果
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  ~ update in-place

Terraform will perform the following actions:

  # aws_iam_user.test_user will be updated in-place
  ~ resource "aws_iam_user" "test_user" {
        id                   = "terraform-actions-test-user"
        name                 = "terraform-actions-test-user"
      ~ tags                 = {
          ~ "Environment" = "production" -> "develop"
        }
      ~ tags_all             = {
          ~ "Environment" = "production" -> "develop"
        }
        # (5 unchanged attributes hidden)
    }

    # Actions to be invoked before this change in order:
    action "aws_sfn_start_execution" "test" {
        config {
            input             = jsonencode(
                {
                    action  = "process"
                    user_id = "12345"
                }
            )
            state_machine_arn = "arn:aws:states:ap-northeast-1:123456789012:stateMachine:terraform-actions-test"
        }
    }


    # Actions to be invoked after this change in order:
    action "aws_sfn_start_execution" "test" {
        config {
            input             = jsonencode(
                {
                    action  = "process"
                    user_id = "12345"
                }
            )
            state_machine_arn = "arn:aws:states:ap-northeast-1:123456789012:stateMachine:terraform-actions-test"
        }
    }


Plan: 0 to add, 1 to change, 0 to destroy. Actions: 2 to invoke.

terraform apply後、Step Functionsの実行結果を見ると、以下のように2回実行されていることが確認できました。

Monosnap_20251022_183347.png

想定通り、1回目はTerraformによる更新前となるので、タグの値がproductionとなっており、2回目はTerraformによる更新後となるので、タグの値がdevelopになっていることが確認できました。

  • 1回目実行結果

Monosnap_20251022_183457.png

  • 2回目実行結果

Monosnap_20251022_183556.png

特定ファイルが更新された場合にStep Functionsを実行

特定のファイルが更新されていたりする際にTerraform actionsを実行する方法について紹介します。

例えばLambdaのソースコードが更新された場合にterraform applyするには、terraform_dataリソース(旧null_resource)でファイルのハッシュ値をTerraformで管理することにより実現できますが、terraform_dataに、先ほどと同じ用にlifecycle設定を行うことで、ファイルが更新されたらTerraform actionsを実行させることができます。

testfileが更新された場合にTerraform actionsを実行
resource "terraform_data" "testfile" {
  input = filesha256("./testfile")

  lifecycle {
    action_trigger {
      events  = [before_update]
      actions = [action.aws_sfn_start_execution.test]
    }
  }
}

ちなみにtestfileの中身を編集後、terraform planを実行した結果は以下となりました。

ファイル編集後のplan結果
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  ~ update in-place

Terraform will perform the following actions:

  # terraform_data.testfile will be updated in-place
  ~ resource "terraform_data" "testfile" {
        id     = "8982fd69-906d-d81a-bba1-86dd53b67296"
      ~ input  = "2ac8479caead590f7f9d483b00b01a02c97dbb8c92cf200ca8dde34bb3d9b312" -> "a5d4217c0fe15c46c10ec56a3fefbeb8c26b8b8885e61cc5b68ac5024fe98641"
      ~ output = "2ac8479caead590f7f9d483b00b01a02c97dbb8c92cf200ca8dde34bb3d9b312" -> (known after apply)
    }

    # Actions to be invoked before this change in order:
    action "aws_sfn_start_execution" "test" {
        config {
            input             = jsonencode(
                {
                    action  = "process"
                    user_id = "12345"
                }
            )
            state_machine_arn = "arn:aws:states:ap-northeast-1:123456789012:stateMachine:terraform-actions-test"
        }
    }


Plan: 0 to add, 1 to change, 0 to destroy. Actions: 1 to invoke.

terraform apply後、Step Functionsの実行結果を見ると、1回実行されていることが確認できました。

おわりに

まだベータ版のため、実環境で使い始めるのはまだまだ先になると思いますが、リソース更新後に追加で行ったりする作業は実際に運用していると結構あるので、一連の運用手順をTerraformでまとめて管理できるのは、かなり有用だなと感じました。

EC2等のセットアップでよく使用するAnsibleについても、Ansible公式のプロバイダでactionブロックに対応するよう開発が進められており、また、Ansible統合管理のAnsible Automation Platformではすでにactionブロックに対応している処理もあるようなので、Ansible Automation PlatformAnsible AWXを使用している方は試してみるのも良いかと思います。

正式リリース版となったら、今まで手作業やAnsible等を使って処理していた運用周りの作業をまとめてTerraformで管理するのもありかなと感じました。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?