EventBridge Rule + Lambda の認可を Terraform で EventBridge 側の IAM ロールに寄せてみた

EventBridge Rule + Lambda の認可を Terraform で EventBridge 側の IAM ロールに寄せてみた

Clock Icon2025.03.17

こんにちは!クラウド事業本部コンサルティング部のたかくに(@takakuni_)です。

先日、Amazon EventBridge Rule がすべてのターゲットタイプの認可に実行ロールを利用できるようになりました。

https://dev.classmethod.jp/articles/amazon-eventbridge-iam-execution-role-all-targets/

アップデートの内容をかいつまんで書くと、「以前までは EventBridge + Lambda のような構成のケースでは、Lambda のリソースベースポリシー側に EventBridge からのアクセスを許可する必要があったものが、 EventBridge のサービスロール側で許可できるようになりました。」といったものです。

EventBridge の IAM ロール側に権限を統一できるようになったため、現在は推奨の認可方法となっています。

私も Terraform で EventBridge + Lambda の構成を更新していきたいなと思いつつ、そもそもこのアップデートは Terraform でサポートされたのかLambda の認可を外さずに変更可能なのか?の2点が気になったためブログにしてみました。

いけた

結論、 AWS プロバイダーのバージョンを上げることなく、 EventBridge + Lambda の構成に対して IAM ロールを使用して認可を行うことができました。

今回は以下のエントリを参考に、リソースベースポリシーから IAM ポリシーへ認可ロジックの設定変更を行ってみたいと思います。

https://dev.classmethod.jp/articles/terraform-creating-s3-and-eventbridge-and-lambda/

EventBridge 用 IAM ロールの作成

まずは EventBridge の IAM ロールを作成します。

IAM ポリシーには対象の Lambda 関数が実行できるよう InvokeFunction API を許可します。

data "aws_iam_policy_document" "eventbridge_assume_policy" {
  statement {
    effect = "Allow"
    principals {
      type        = "Service"
      identifiers = ["events.amazonaws.com"]
    }
    actions = ["sts:AssumeRole"]
  }
}

resource "aws_iam_role" "eventbridge_service_role" {
  name               = local.eventbridge_service_role
  assume_role_policy = data.aws_iam_policy_document.eventbridge_assume_policy.json
}

data "aws_iam_policy_document" "eventbridge_iam_policy" {
  statement {
    effect = "Allow"
    actions = [
      "lambda:InvokeFunction"
    ]
    resources = [
      aws_lambda_function.test_lambda.arn
    ]
  }
}

resource "aws_iam_policy" "eventbridge_iam_policy" {
  name   = "${local.eventbridge_service_role}-policy"
  policy = data.aws_iam_policy_document.eventbridge_iam_policy.json
}

resource "aws_iam_role_policy_attachment" "eventbridge_iam_policy" {
  role       = aws_iam_role.eventbridge_service_role.name
  policy_arn = aws_iam_policy.eventbridge_iam_policy.arn
}

EventBridge のロール ARN を指定

ワークロードにもよりますが、 EventBridge に IAM ロールが付与される前に、 Lambda の認可が外れてエラーになるのを防ぎたいため、デプロイは多段に行います。

まずは先ほど作成した IAM ロールを EventBridge に付与するようコードを修正します。

resource "aws_cloudwatch_event_rule" "eventbridge_rule" {
  name        = local.event_rule
  description = "S3の特定フォルダ配下にファイルアップロードしたときに、Lambdaを起動するルールをIaC化"

  event_pattern = jsonencode({
    "detail-type" : ["Object Created"],
    "source" : ["aws.s3"],
    "detail" : {
      "bucket" : {
        "name" : [local.bucket_name]
      },
      "object" : {
        "key" : [{
          "prefix" : "input-file/"
        }]
      }
    }
  })
}

resource "aws_cloudwatch_event_target" "eventbridge_target" {
  rule = aws_cloudwatch_event_rule.eventbridge_rule.name

  arn = aws_lambda_function.test_lambda.arn
  input_transformer {
    input_paths = {
      "input_bucket_name" : "$.detail.bucket.name",
      "input_s3_key" : "$.detail.object.key"
    }
    input_template = <<TEMPLATE
{"Parameters": {"input_bucket_name":"<input_bucket_name>", "input_s3_key":"<input_s3_key>"}}
    TEMPLATE
  }
+  role_arn = aws_iam_role.eventbridge_service_role.arn
}

これにより、Lambda のリソースベースポリシーが外れても、 EventBridge 側の IAM ロールで認可されるよう変更できました。

takakuni.shinnosuke@HL01556 hoge-tf % terraform apply

data.archive_file.function_info: Reading...
data.archive_file.function_info: Read complete after 0s [id=0f3f30b7a97511a3cebbe3e869c7be793e108599]
data.aws_iam_policy.iam_policy_AWSLambdaBasicExecutionRole: Reading...
data.aws_iam_policy_document.eventbridge_assume_policy: Reading...
data.aws_iam_policy_document.assume_role: Reading...
aws_cloudwatch_log_group.cloudwatch_log: Refreshing state... [id=/aws/lambda/hoge-tf]
aws_cloudwatch_event_rule.eventbridge_rule: Refreshing state... [id=hoge-tf-event-rule]
data.aws_iam_policy_document.eventbridge_assume_policy: Read complete after 0s [id=3959961479]
data.aws_iam_policy_document.assume_role: Read complete after 0s [id=2690255455]
aws_s3_bucket.bucket: Refreshing state... [id=hoge-tf-bucket]
aws_iam_role.iam_role_for_lambda: Refreshing state... [id=iam_for_lambda]
aws_iam_role.eventbridge_service_role: Refreshing state... [id=hoge-tf-eventbridge-service-role]
aws_s3_bucket_notification.bucket_notification: Refreshing state... [id=hoge-tf-bucket]
aws_s3_object.input_folder: Refreshing state... [id=input-file/]
data.aws_iam_policy.iam_policy_AWSLambdaBasicExecutionRole: Read complete after 1s [id=arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole]
aws_iam_policy.iam_policy_AWSLambdaBasicExecutionRole: Refreshing state... [id=arn:aws:iam::622809842341:policy/hoge-tf-lambda-service-role]
aws_iam_role_policy_attachment.lambda_policy: Refreshing state... [id=iam_for_lambda-20250317090140160700000001]
aws_lambda_function.test_lambda: Refreshing state... [id=hoge-tf]
aws_cloudwatch_event_target.eventbridge_target: Refreshing state... [id=hoge-tf-event-rule-terraform-20250317090154875700000002]
data.aws_iam_policy_document.eventbridge_iam_policy: Reading...
data.aws_iam_policy_document.eventbridge_iam_policy: Read complete after 0s [id=1400862687]
aws_iam_policy.eventbridge_iam_policy: Refreshing state... [id=arn:aws:iam::622809842341:policy/hoge-tf-eventbridge-service-role-policy]
aws_iam_role_policy_attachment.eventbridge_iam_policy: Refreshing state... [id=hoge-tf-eventbridge-service-role-20250317090155917800000003]

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_cloudwatch_event_target.eventbridge_target will be updated in-place
  ~ resource "aws_cloudwatch_event_target" "eventbridge_target" {
        id             = "hoge-tf-event-rule-terraform-20250317090154875700000002"
      + role_arn       = "arn:aws:iam::622809842341:role/hoge-tf-eventbridge-service-role"
        # (7 unchanged attributes hidden)

        # (1 unchanged block hidden)
    }

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

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

aws_cloudwatch_event_target.eventbridge_target: Modifying... [id=hoge-tf-event-rule-terraform-20250317090154875700000002]
aws_cloudwatch_event_target.eventbridge_target: Modifications complete after 0s [id=hoge-tf-event-rule-terraform-20250317090154875700000002]

Apply complete! Resources: 0 added, 1 changed, 0 destroyed.

試しにオブジェクトをアップロードしてみましたが、共存していてもうまく動いていますね。

2025-03-17 at 18.21.20-CloudWatch  ap-northeast-1.png

Lambda のポリシー変更

EventBridge 側の IAM ロールで許可できているため、 Lambda のリソースベースポリシーを変更します。

data "aws_iam_policy" "iam_policy_AWSLambdaBasicExecutionRole" {
  arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}

resource "aws_iam_policy" "iam_policy_AWSLambdaBasicExecutionRole" {
  name   = local.lambda_service_role_name
  policy = data.aws_iam_policy.iam_policy_AWSLambdaBasicExecutionRole.policy
}

resource "aws_iam_role_policy_attachment" "lambda_policy" {
  role       = aws_iam_role.iam_role_for_lambda.name
  policy_arn = aws_iam_policy.iam_policy_AWSLambdaBasicExecutionRole.arn
}

data "aws_iam_policy_document" "assume_role" {
  statement {
    effect = "Allow"

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

    actions = ["sts:AssumeRole"]
  }
}

resource "aws_iam_role" "iam_role_for_lambda" {
  name               = "iam_for_lambda"
  assume_role_policy = data.aws_iam_policy_document.assume_role.json
}

data "archive_file" "function_info" {
  type        = "zip"
  source_file = "${local.lambda_file_name}.py"
  output_path = local.output_source_dir
}

resource "aws_cloudwatch_log_group" "cloudwatch_log" {
  name = "/aws/lambda/${local.lambda_func}"
}

- resource "aws_lambda_permission" "logging" {
-   action        = "lambda:InvokeFunction"
-   function_name = aws_lambda_function.test_lambda.function_name
-   principal     = "events.amazonaws.com"
-   source_arn    = aws_cloudwatch_event_rule.eventbridge_rule.arn
- }

resource "aws_lambda_function" "test_lambda" {
  function_name = local.lambda_func
  role          = aws_iam_role.iam_role_for_lambda.arn
  filename      = data.archive_file.function_info.output_path
  handler       = "${local.lambda_file_name}.${local.entry_point}"
  runtime       = local.lambda_runtime

  source_code_hash = data.archive_file.function_info.output_base64sha256

  depends_on = [aws_iam_role_policy_attachment.lambda_policy, aws_cloudwatch_log_group.cloudwatch_log]
}

うまく削除できていますね。

takakuni.shinnosuke@HL01556 hoge-tf % terraform apply

data.archive_file.function_info: Reading...
data.archive_file.function_info: Read complete after 0s [id=0f3f30b7a97511a3cebbe3e869c7be793e108599]
data.aws_iam_policy_document.assume_role: Reading...
data.aws_iam_policy.iam_policy_AWSLambdaBasicExecutionRole: Reading...
data.aws_iam_policy_document.eventbridge_assume_policy: Reading...
aws_cloudwatch_log_group.cloudwatch_log: Refreshing state... [id=/aws/lambda/hoge-tf]
aws_cloudwatch_event_rule.eventbridge_rule: Refreshing state... [id=hoge-tf-event-rule]
aws_lambda_permission.logging: Refreshing state... [id=terraform-20250317092401922000000001]
data.aws_iam_policy_document.assume_role: Read complete after 0s [id=2690255455]
data.aws_iam_policy_document.eventbridge_assume_policy: Read complete after 0s [id=3959961479]
aws_s3_bucket.bucket: Refreshing state... [id=hoge-tf-bucket]
aws_iam_role.iam_role_for_lambda: Refreshing state... [id=iam_for_lambda]
aws_iam_role.eventbridge_service_role: Refreshing state... [id=hoge-tf-eventbridge-service-role]
aws_s3_bucket_notification.bucket_notification: Refreshing state... [id=hoge-tf-bucket]
aws_s3_object.input_folder: Refreshing state... [id=input-file/]
data.aws_iam_policy.iam_policy_AWSLambdaBasicExecutionRole: Read complete after 1s [id=arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole]
aws_iam_policy.iam_policy_AWSLambdaBasicExecutionRole: Refreshing state... [id=arn:aws:iam::622809842341:policy/hoge-tf-lambda-service-role]
aws_iam_role_policy_attachment.lambda_policy: Refreshing state... [id=iam_for_lambda-20250317090140160700000001]
aws_lambda_function.test_lambda: Refreshing state... [id=hoge-tf]
data.aws_iam_policy_document.eventbridge_iam_policy: Reading...
aws_cloudwatch_event_target.eventbridge_target: Refreshing state... [id=hoge-tf-event-rule-terraform-20250317090154875700000002]
data.aws_iam_policy_document.eventbridge_iam_policy: Read complete after 0s [id=1400862687]
aws_iam_policy.eventbridge_iam_policy: Refreshing state... [id=arn:aws:iam::622809842341:policy/hoge-tf-eventbridge-service-role-policy]
aws_iam_role_policy_attachment.eventbridge_iam_policy: Refreshing state... [id=hoge-tf-eventbridge-service-role-20250317090155917800000003]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  - destroy

Terraform will perform the following actions:

  # aws_lambda_permission.logging will be destroyed
  # (because aws_lambda_permission.logging is not in configuration)
  - resource "aws_lambda_permission" "logging" {
      - action              = "lambda:InvokeFunction" -> null
      - function_name       = "hoge-tf" -> null
      - id                  = "terraform-20250317092401922000000001" -> null
      - principal           = "events.amazonaws.com" -> null
      - source_arn          = "arn:aws:events:ap-northeast-1:622809842341:rule/hoge-tf-event-rule" -> null
      - statement_id        = "terraform-20250317092401922000000001" -> null
      - statement_id_prefix = "terraform-" -> null
        # (1 unchanged attribute hidden)
    }

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

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

aws_lambda_permission.logging: Destroying... [id=terraform-20250317092401922000000001]
aws_lambda_permission.logging: Destruction complete after 0s

Apply complete! Resources: 0 added, 0 changed, 1 destroyed.

再度、S3 にオブジェクトをアップロードしたところ、こちらも問題なく Lambda が動いていますね。

まとめ

以上、「EventBridge + Lambda の認可を Terraform で EventBridge 側の IAM ロールに寄せてみた」でした。

新しい開発は問題なく IAM ロール側に設定できると思いますが、既存の設定と混在するのは避けたいです。

安全に認可を変更できる手順を考えてみましたが、参考になれば幸いです。

クラウド事業本部コンサルティング部のたかくに(@takakuni_)でした!

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.