Posted at

[Terraform] DynamoDBをトリガーとしたLambdaの設定方法

More than 1 year has passed since last update.


やりたいこと

記事タイトルそのままです。

DynamoDBのデータ更新をトリガーとして起動されるLambdaを、Terraformで設定したい。


tfファイルの記述例


IAM


aws.iam.tf

// Lambda用ロール

resource "aws_iam_role" "sample-lambda-role" {
name = "sample-lambda-dynamodb-trigger"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
}

// Lambdaログ出力権限
resource "aws_iam_role_policy" "sample-lambda-log-output" {
role = "${aws_iam_role.sample-lambda-role.id}"
name = "lambda-log-output"
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:*:*:*",
"Effect": "Allow"
}
]
}
EOF
}

// DynamoDB Stream の読み込み権限
resource "aws_iam_role_policy" "sample-dynamodb-stream" {
role = "${aws_iam_role.sample-lambda-role.id}"
name = "dynamodb-stream"
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"dynamodb:DescribeStream",
"dynamodb:GetRecords",
"dynamodb:GetShardIterator",
"dynamodb:ListStreams"
],
"Resource": "${aws_dynamodb_table.sample-table.stream_arn}",
"Effect": "Allow"
}
]
}
EOF
}




  • DynamoDB Stream からデータを読み取るためのポリシーが必要です。この権限が付いていないと、トリガー設定でコケます。


Lambda


aws.lambda.tf

// Lambda Function を ZIP化

data "archive_file" "lambda-zip_sample-function" {
type = "zip"
output_path = "./_zip/sample-function.zip"
source {
filename = "lambda_function.py"
content = <<EOF
import json
def lambda_handler(event, context):
result = json.dumps(event)
print(result)
return result
EOF
}
}

// Lambda Function 作成
resource "aws_lambda_function" "sample-function" {
function_name = "sample-function-name"
handler = "lambda_function.lambda_handler"
filename = "${data.archive_file.lambda-zip_sample-function.output_path}"
source_code_hash = "${data.archive_file.lambda-zip_sample-function.output_base64sha256}"
memory_size = 128
timeout = 300
runtime = "python3.6"
role = "${aws_iam_role.sample-lambda-role.arn}"
description = "Dynamo Trigger Test"
}



  • この記述例ではイベント情報を出力するだけの Lambda Function をtfファイルに直書きしています。
    zipファイルの置き場として、ディレクトリ ./_zip/ が必要。

  • functionファイルを作成しておいて archive_file でZIP化しても、あらかじめzipファイルを用意しておいても構いません。


DynamoDB


aws.dynamodb.tf


resource "aws_dynamodb_table" "sample-table" {
name = "sample-table-name"
read_capacity = 1
write_capacity = 1
hash_key = "key1"
range_key = "key2"
attribute {
name = "key1"
type = "S"
}
attribute {
name = "key2"
type = "S"
}
stream_enabled = true
stream_view_type = "NEW_AND_OLD_IMAGES"
}

//
resource "aws_lambda_event_source_mapping" "sample-table-trigger" {
depends_on = ["aws_dynamodb_table.sample-table", "aws_iam_role_policy.sample-dynamodb-stream"]
batch_size = 100
event_source_arn = "${aws_dynamodb_table.sample-table.stream_arn}"
enabled = true
function_name = "${aws_lambda_function.sample-function.arn}"
starting_position = "TRIM_HORIZON"
}




  • aws_dynamodb_table に下記のパラメータを記述して、DynamoDB Stream を有効化。


    • stream_enabled = true

    • stream_view_type = (任意)




  • aws_lambda_event_source_mapping で Lambdaのトリガーとして DynamoDB Stream を追加。


  • aws_lambda_event_source_mapping は Lambda が主語のような気はするものの、DynamoDB Stream の存在に依存する設定なので、DynamoDB用のtfファイルにまとめてます。個人的な好みです。


注意事項


一発目はこれでOK。

Terraform実行一発目から上記内容で設定する場合は、問題なく設定できるはずです。


DynamoDB Stream を後付けする場合はNG。

既にDynamoDBのTableを作成済みで、後から stream_enabled = true を追加設定する場合は、単純にtfファイルに上記の通り記述して実行しても上手く行きません。(Terraform v0.11.3 時点)

下記のようなエラーが発生するはずです。

aws_iam_role_policy.sample-dynamodb-stream: Resource 'aws_dynamodb_table.sample-table' does not have attribute 'stream_arn' for variable 'aws_dynamodb_table.sample-table.stream_arn'

aws_dynamodb_tablestream_arn が無いよ!」と怒られてます。


なぜ?

Terraform は、Terraform を利用して設定したリソースの情報をtfstateファイルに保持しています。

そして次回実行するときは、tfstateファイルに保持されている現在のリソース情報と、新たなtfファイルの内容を突き合わせて最小限のアップデートをします。

その時、リソース情報の整合性チェック等(Plan)を行ってから、設定反映(Apply)を実行する仕組みになっています。

DynamoDB Stream の後付けと同時にmappingをしようとすると、現状としては stream_arn が存在しないのにそれを利用してリソースを設定しようとしているため、Plan の段階でNGになります。

「Planで stream_arn を補完してくれればいいのに」とは思いますが、Stream名には作成日時が自動的に入るので、実際に作ってからでないと確定できないんですよね。

... というのが原因と考えられます。

Terraform のソースを見れば原因がハッキリわかるんでしょうが、私にはそこまでのモチベーションは無いです。


では、どうすれば?

段階を踏んで実行しましょう。

1. stream_enabled = true で実行しDynamoDB Stream を有効化する。これで次回から stream_arn が参照できるようになる。

2. DynamoDB Stream の ARN に依存するリソース設定を追加する。

もしかしたら Terraform の実行オプションで回避出来る等、他に方法があるかもしれませんが、私には見つけられませんでした。