やりたいこと
記事タイトルそのままです。
DynamoDBのデータ更新をトリガーとして起動されるLambdaを、Terraformで設定したい。
tfファイルの記述例
-
使用tfリソース一覧
tfファイルは
IAM
,Lambda
,DynamoDB
で分割しています。そのへんはお好みで。オプション等は、作成したいものに合わせて適宜変更。
IAM
// 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
// 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
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_table
に stream_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 の実行オプションで回避出来る等、他に方法があるかもしれませんが、私には見つけられませんでした。