
EventBridge Rule + Lambda の認可を Terraform で EventBridge 側の IAM ロールに寄せてみた
こんにちは!クラウド事業本部コンサルティング部のたかくに(@takakuni_)です。
先日、Amazon EventBridge Rule がすべてのターゲットタイプの認可に実行ロールを利用できるようになりました。
アップデートの内容をかいつまんで書くと、「以前までは EventBridge + Lambda のような構成のケースでは、Lambda のリソースベースポリシー側に EventBridge からのアクセスを許可する必要があったものが、 EventBridge のサービスロール側で許可できるようになりました。」といったものです。
EventBridge の IAM ロール側に権限を統一できるようになったため、現在は推奨の認可方法となっています。
私も Terraform で EventBridge + Lambda の構成を更新していきたいなと思いつつ、そもそもこのアップデートは Terraform でサポートされたのか
、Lambda の認可を外さずに変更可能なのか?
の2点が気になったためブログにしてみました。
いけた
結論、 AWS プロバイダーのバージョンを上げることなく、 EventBridge + Lambda の構成に対して IAM ロールを使用して認可を行うことができました。
今回は以下のエントリを参考に、リソースベースポリシーから IAM ポリシーへ認可ロジックの設定変更を行ってみたいと思います。
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.
試しにオブジェクトをアップロードしてみましたが、共存していてもうまく動いていますね。
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_)でした!