この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
S3イベント通知機能で、S3へのファイルアップロードをSNS→SQSへ通知する処理をTerraformで作成する機会がありましたので、レポートします。
作成するリソースの概要
- もちろんですが、S3、SNS、SQSを作成します。SNSについてはトピックとサブスクリプションが必要です。
- 各リソース間のアクセス許可が必要です。
- S3イベント通知からSNSトピックへイベントを送信するには、SNSトピックのアクセスポリシーでS3がSNSメッセージを発行するのを許可する必要があります。
- SNSからSQSへメッセージを渡すのにも、SQSキューのアクセス許可設定にてSNSからのメッセージ送信を許可する必要があります。
コード内容解説
S3
resource aws_s3_bucket main {
bucket_prefix = "event-notification"
acl = "private"
force_destroy = true
}
resource aws_s3_bucket_public_access_block main {
bucket = aws_s3_bucket.main.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
aws_s3_bucket_public_access_block
はS3イベント通知とは関係ありませんが、以下のエントリにあるように基本的に設定しておくべきだと思っているので、設定しています。
SNSトピック
resource aws_sns_topic s3_put_object_notification {
name = "event-notification-test-topic"
}
特に解説することはありません。キチンとロギングしたいのであれば xxx_success_feedback_role_arn
xxx_failure_feedback_role_arn
xxx_success_feedback_sample_rate
といったargumentを指定してCloudWatch Logsに実行ログを送ることを検討しましょう。(xxxにはサブスクライブするリソースの種類、つまりlambda/sqs/http/applicationが入ります。)
SNSトピックのアクセスポリシー
S3イベント通知からSNSメッセージを発行できるようにするポリシーの設定です。
resource aws_sns_topic_policy from-s3 {
arn = aws_sns_topic.s3_put_object_notification.arn
policy = templatefile(
"./sns_policy_triggered_by_s3_event_notification.json",
{
sns-arn = aws_sns_topic.s3_put_object_notification.arn,
bucket-arn = aws_s3_bucket.main.arn
}
)
}
sns_policy_triggered_by_s3_event_notification.json
{
"Version":"2012-10-17",
"Statement":[{
"Effect": "Allow",
"Principal": {"Service":"s3.amazonaws.com"},
"Action": "SNS:Publish",
"Resource": "${sns-arn}",
"Condition":{
"ArnLike":{"aws:SourceArn":"${bucket-arn}"}
}
}]
}
今気づきましたが、リソースベースのポリシーでResource
を指定する(7行目)の、意味ないですね…
S3イベント通知
resource aws_s3_bucket_notification put_object {
bucket = aws_s3_bucket.main.bucket
topic {
topic_arn = aws_sns_topic.s3_put_object_notification.arn
events = [
"s3:ObjectCreated:Put",
]
}
}
ここまでで、S3イベント通知→SNSが実現できました。
以降はSNS→SQSの部分です。
SQSキュー
resource aws_sqs_queue s3_put_object_notification {
name = "put-object-notification-queue"
visibility_timeout_seconds = 300
message_retention_seconds = 60 * 60 * 24 * 7 * 2
receive_wait_time_seconds = 20
redrive_policy = jsonencode({
deadLetterTargetArn = aws_sqs_queue.s3_put_object_notification_dlq.arn
maxReceiveCount = 4
})
}
resource aws_sqs_queue s3_put_object_notification_dlq {
name = "put-object-notification-dlq"
visibility_timeout_seconds = 300
message_retention_seconds = 60 * 60 * 24 * 7 * 2
receive_wait_time_seconds = 20
}
resource aws_cloudwatch_metric_alarm dlq_recieved_message {
alarm_name = "put-object-notification-dlq-recieved-message"
comparison_operator = "GreaterThanOrEqualToThreshold"
evaluation_periods = "1"
metric_name = "ApproximateNumberOfMessagesVisible"
namespace = "AWS/SQS"
period = "60"
statistic = "Sum"
threshold = "1"
alarm_description = "dead letter queue received message"
alarm_actions = [aws_sns_topic.dlq_recieved_message_notification.arn]
insufficient_data_actions = []
dimensions = {
QueueName = aws_sqs_queue.s3_put_object_notification_dlq.name
}
}
resource aws_sns_topic dlq_recieved_message_notification {
name = "dlq-recieved-message-notification-topic"
}
エラーを補足するためredrive_policy
でデッドレターキューを設定しています。
さらにデッドレターキューにメッセージが入ったことに気づくためCloudWatchAlarm→SNSでメール通知をします。
SNSトピックのEメールサブスクリプションはTerraformでは実装できません。メールでの承認後にしかARNが生成されず、それがTerraformの仕様に合わないためだとのことです。こちらのページのProtocols supported欄最下部に説明があります。そのためEメールサブスクリプションはTerraform外で作成してください。
SQSキューのアクセス許可
SNSにSQSメッセージを送信できる権限を与えます。
resource aws_sqs_queue_policy s3-put-s3_put_object_notification-notification {
queue_url = aws_sqs_queue.s3_put_object_notification.id
policy = templatefile(
"./sqs_policy.json",
{
sqs-arn = aws_sqs_queue.s3_put_object_notification.arn,
sns-arn = aws_sns_topic.s3_put_object_notification.arn
}
)
}
sqs_policy.json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "sns.amazonaws.com"
},
"Action": "sqs:SendMessage",
"Resource": "${sqs-arn}",
"Condition": {
"ArnEquals": {
"aws:SourceArn": "${sns-arn}"
}
}
}
]
}
これも別にResource
句要らないですね。
SQSのSNSトピックサブスクリプション
resource aws_sns_topic_subscription sqs {
topic_arn = aws_sns_topic.s3_put_object_notification.arn
protocol = "sqs"
endpoint = aws_sqs_queue.s3_put_object_notification.arn
}
このSNSトピックサブスクリプションに対してデッドレターキューを設定することも可能です。が、Terraformは2020年3月末現在(aws provider version 2.55.0)未対応のようです。(おそらくPRはこれ)
SNSトピックサブスクリプションのデッドレターキューについては以下を参照ください。
コード全体
# S3
resource aws_s3_bucket main {
bucket_prefix = "event-notification"
acl = "private"
force_destroy = true
}
resource aws_s3_bucket_public_access_block main {
bucket = aws_s3_bucket.main.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
# SNSトピック
resource aws_sns_topic s3_put_object_notification {
name = "event-notification-test-topic"
}
# SNSトピックのアクセスポリシー
resource aws_sns_topic_policy from_s3 {
arn = aws_sns_topic.s3_put_object_notification.arn
policy = templatefile(
"./sns_policy_triggered_by_s3_event_notification.json",
{
sns-arn = aws_sns_topic.s3_put_object_notification.arn,
bucket-arn = aws_s3_bucket.main.arn
}
)
}
# S3イベント通知
resource aws_s3_bucket_notification put_object {
bucket = aws_s3_bucket.main.bucket
topic {
topic_arn = aws_sns_topic.s3_put_object_notification.arn
events = [
"s3:ObjectCreated:Put",
]
}
}
# SQSキュー
resource aws_sqs_queue s3_put_object_notification {
name = "put-object-notification-queue"
visibility_timeout_seconds = 300
message_retention_seconds = 60 * 60 * 24 * 7 * 2
receive_wait_time_seconds = 20
redrive_policy = jsonencode({
deadLetterTargetArn = aws_sqs_queue.s3_put_object_notification_dlq.arn
maxReceiveCount = 4
})
}
resource aws_sqs_queue s3_put_object_notification_dlq {
name = "put-object-notification-dlq"
visibility_timeout_seconds = 300
message_retention_seconds = 60 * 60 * 24 * 7 * 2
receive_wait_time_seconds = 20
}
resource aws_cloudwatch_metric_alarm dlq_recieved_message {
alarm_name = "put-object-notification-dlq-recieved-message"
comparison_operator = "GreaterThanOrEqualToThreshold"
evaluation_periods = "1"
metric_name = "ApproximateNumberOfMessagesVisible"
namespace = "AWS/SQS"
period = "60"
statistic = "Sum"
threshold = "1"
alarm_description = "dead letter queue received message"
alarm_actions = [aws_sns_topic.dlq_recieved_message_notification.arn]
insufficient_data_actions = []
dimensions = {
QueueName = aws_sqs_queue.s3_put_object_notification_dlq.name
}
}
resource aws_sns_topic dlq_recieved_message_notification {
name = "dlq-recieved-message-notification-topic"
}
# SQSキューのアクセス許可
resource aws_sqs_queue_policy s3_put_object_notification {
queue_url = aws_sqs_queue.s3_put_object_notification.id
policy = templatefile(
"./sqs_policy.json",
{
sqs-arn = aws_sqs_queue.s3_put_object_notification.arn,
sns-arn = aws_sns_topic.s3_put_object_notification.arn
}
)
}
# SQSのSNSトピックサブスクリプション
resource aws_sns_topic_subscription sqs {
topic_arn = aws_sns_topic.s3_put_object_notification.arn
protocol = "sqs"
endpoint = aws_sqs_queue.s3_put_object_notification.arn
}
sns_policy_triggered_by_s3_event_notification.json
{
"Version":"2012-10-17",
"Statement":[{
"Effect": "Allow",
"Principal": {"Service":"s3.amazonaws.com"},
"Action": "SNS:Publish",
"Resource": "${sns-arn}",
"Condition":{
"ArnLike":{"aws:SourceArn":"${bucket-arn}"}
}
}]
}
sqs_policy.json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "sns.amazonaws.com"
},
"Action": "sqs:SendMessage",
"Resource": "${sqs-arn}",
"Condition": {
"ArnEquals": {
"aws:SourceArn": "${sns-arn}"
}
}
}
]
}
あわせて読みたい
S3イベント通知とSQSの間にSNSを挟んだ理由は以下をご参照ください。