新旧 S3 オブジェクトの差分を Lambda 関数で取得し SNS で通知してみた
はじめに
テクニカルサポートの 片方 です。
バージョニング有効化済みの S3 バケットで新バージョンのオブジェクトがアップロードされた際に Lambda 関数を利用して差分を取得したいと思います。そして取得した差分を SNS で通知してみました。
構成としては以下のようになります。
やってみた
SNS トピックの作成、トピックのサブスクリプションの作成は省かせいただきます。
しかしながら、作成したトピックの arn は後ほど必要になるのでメモしてください。
ロール
作成する Lamada 関数にアタッチする実行ロールを作成します。
Lambda 関数作成時に選択する、デフォルトの実行ロールの変更において、「基本的な Lambda アクセス権限で新しいロールを作成」を選択してください。
その後、作成された当該ロールにカスタマー管理ポリシーとして以下をアタッチしてください。
ポリシー例
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:ListBucket",
"s3:GetObject",
"s3:ListBucketVersions",
"s3:GetObjectVersion"
],
"Resource": [
"arn:aws:s3:::対象バケット名",
"arn:aws:s3:::対象バケット名/*"
]
}
]
}
Lambda 関数
今回は Python 3.12 で作成しました。
サンプルコード
import boto3
import difflib
s3 = boto3.client('s3')
sns = boto3.client('sns')
def lambda_handler(event, context):
try:
bucket = event['Records'][0]['s3']['bucket']['name']
key = event['Records'][0]['s3']['object']['key']
except KeyError:
return {'message': 'Event structure is not as expected'}
try:
versions = s3.list_object_versions(Bucket=bucket, Prefix=key)
except s3.exceptions.ClientError:
return {'message': 'Failed to list object versions'}
if 'Versions' not in versions or len(versions['Versions']) < 2:
return {'message': 'No previous version available for comparison'}
latest_versions = sorted(versions['Versions'], key=lambda v: v['LastModified'], reverse=True)[:2]
latest_version_id = latest_versions[0]['VersionId']
previous_version_id = latest_versions[1]['VersionId']
latest_object = s3.get_object(Bucket=bucket, Key=key, VersionId=latest_version_id)['Body'].read().decode('utf-8')
previous_object = s3.get_object(Bucket=bucket, Key=key, VersionId=previous_version_id)['Body'].read().decode('utf-8')
diff = difflib.ndiff(previous_object.splitlines(), latest_object.splitlines())
diff_result = '\n'.join(line for line in diff if line.startswith('+ ') or line.startswith('- '))
message = f"""
S3 バケット名: {bucket}
オブジェクト名: {key}
変更箇所
{diff_result}
旧バージョン
{previous_object}
"""
sns.publish(
TopicArn='作成した SNS トピックの arn を記載',
Message=message,
Subject='S3 Object Version Difference Notification'
)
return {'message': 'Diff computed and notification sent'}
S3 イベント通知
バージョニング有効化済み対象 S3 バケットにおいてオブジェクトが更新された際に、イベント通知を利用して作成した Lambda 関数を呼び出します。
対象 S3 バケットのプロパティを選択します。
イベント通知を作成をクリックします。
イベント名を記載して、イベントタイプは PUT のみ選択します。
送信先セクションで Lambda 関数を選択し、作成した Lambda 関数を選択します。
問題なければ、作成してください。
これで、終了です。お疲れさまでした!
検証してみた
対象バケットに適当なオブジェクト(ファイル)をアップロードします。
ファイルの内容を変更します。
変更後のオブジェクト(ファイル)を S3 バケットへアップロードします。その後、新旧バージョンが存在することを確認します。
SNS で設定した通知先へ届いていることを確認しました。成功です!
通知例
※ 一部マスクします
S3 バケット名: test-20240523-1700
オブジェクト名: Sample-test.txt
変更箇所
+ text-ver2だよ。
+ 本日は晴天なり。
- textだよ。
- テキストだよ。
- テストだよ。
旧バージョン
textだよ。
テキストだよ。
テストだよ。
--
このトピックからの通知の受信を停止したい場合は、以下のリンクをクリックまたはアクセスして登録解除してください:
https://sns.ap-northeast-1.ama zonaws.com/unsubscribe.html? SubscriptionArn=arn:aws:sns: ap-northeast-1:123456789012: Test-Event:7edxxxxx-1114-4444- 98b3absde123456c4&[email protected]
このメールに直接返信しないでください。このメールに関してご質問やご意見がございましたら、https://aws.amazon.com/supportまでお問い合わせください。
補足
例えば、以下のような記述を追加することで、現在のオブジェクト内容も通知に含むことが可能です。
message = f"""
S3 バケット名: {bucket}
オブジェクト名: {key}
変更箇所
{diff_result}
旧バージョン
{previous_object}
新バージョン
{latest_object}
まとめ
環境変数を利用して、SNS で通知する方法などもご検討ください。また、ご自身の環境に合わせて通知文などを適宜修正の上ご利用ください。
本ブログが誰かのご参考になれば幸いです。
参考資料
- Amazon SNS の開始方法 - Amazon Simple Notification Service
- S3 - Boto3 1.34.157 documentation
- SNS - Boto3 1.34.157 documentation
- Amazon S3 イベント通知 - Amazon Simple Storage Service
アノテーション株式会社について
アノテーション株式会社は、クラスメソッド社のグループ企業として「オペレーション・エクセレンス」を担える企業を目指してチャレンジを続けています。「らしく働く、らしく生きる」のスローガンを掲げ、様々な背景をもつ多様なメンバーが自由度の高い働き方を通してお客様へサービスを提供し続けてきました。現在当社では一緒に会社を盛り上げていただけるメンバーを募集中です。少しでもご興味あれば、アノテーション株式会社WEBサイトをご覧ください。