はじめに
この記事は、SAM版ハンズオン記事の比較版です。
SAM版ではコマンド数本でS3バケット・Lambda・イベント通知設定が完了しましたが、「SAMが裏で何をやっているのか?」「バケット名のグローバルユニーク問題はどう解決する?」「再帰呼び出しの警告って何?」 といった疑問を実際のコンソール操作を通じて体験できるのがこの記事です。
この記事を読むとわかること:
- S3・Lambda を個別に設定する順序と依存関係
- SAMの
!Sub "${AWS::StackName}-upload-${AWS::AccountId}"が解決していること(バケット名のグローバルユニーク問題) - 「再帰呼び出し」警告の意味と、今回は無視してよい理由
- S3バケット削除時に「先に空にする」が必要な理由
SAM vs コンソール:どれだけ違うか
| 比較項目 | SAM(コード) | コンソール(手動) |
|---|---|---|
| バケット名の一意性確保 | !Sub "...-${AWS::AccountId}" で自動 | 手動でグローバルユニークな名前を考える |
| S3イベント通知設定 | Type: S3 の1行 | Lambda → トリガーを追加(画面操作) |
| リソースベースポリシー | 自動追加 | トリガー追加時に自動追加(画面の警告を確認する必要あり) |
| 削除 | バケットを空にしてから sam delete | S3オブジェクト削除 → バケット削除 → Lambda削除 × 個別操作 |
構築するもの
SAM版と全く同じ構成・機能のファイルアップロードトリガー環境を構築します。
ファイルアップロード(コンソール / AWS CLI)
↓ S3 ObjectCreated イベント発火
S3 バケット(my-s3-event-upload)
↓ Lambda を呼び出し
Lambda(S3EventFunction / Python 3.12)
↓ ファイル情報(バケット名・ファイル名・サイズ)をログ出力
CloudWatch Logs全体の作業順序
① S3 バケット作成
↓
② Lambda 関数作成(コード入力)
↓
③ S3 トリガーを Lambda に追加(リソースベースポリシーも自動設定)
↓
④ ファイルアップロードして動作テスト
⑤ リソースの削除(S3を先に空にする)順序の理由:
③でS3トリガーを追加する際に「どのバケットを対象にするか」を選択するため、①でバケットを先に作成しておく必要があります。SQSハンズオンのようにARNのコピーが必要な場面はありませんが、バケットが存在しないとトリガー設定画面で選択できません。
① S3 バケット作成
AWSコンソール → S3 → 「バケットを作成」
1-1. バケットの設定
| 設定項目 | 値 |
|---|---|
| バケット名 | my-s3-event-upload(任意。グローバルで一意な名前) |
| AWSリージョン | アジアパシフィック(東京)ap-northeast-1 |
| オブジェクト所有者 | ACL 無効(デフォルトのまま) |
| パブリックアクセスのブロック | すべてブロック(デフォルトのまま) |
| バージョニング | 無効(デフォルトのまま) |
| 暗号化 | Amazon S3 マネージドキー(デフォルトのまま) |
「バケットを作成」をクリックします。
バケット名のルール(SAMとの違いが出るポイント):
S3のバケット名はAWS全体(全リージョン・全アカウント)でグローバルユニークである必要があります。
- 小文字・数字・ハイフンのみ使用可(大文字・アンダースコアは使えない)
my-s3-event-uploadだと他のユーザーがすでに使っている可能性がある一意にする推奨の方法:
my-s3-event-upload-自分のAWSアカウントID(最も確実)my-s3-event-upload-20260222(日付を付ける)SAMでは
!Sub "${AWS::StackName}-upload-${AWS::AccountId}"で自動的にアカウントIDを付加しています。コンソールではこのルールを頭の中で考えて手動入力する必要があります。
控えておく情報:
- バケット名(例:
my-s3-event-upload)
② Lambda 関数作成
AWSコンソール → Lambda → 「関数の作成」
2-1. 基本設定
| 設定項目 | 値 |
|---|---|
| 作成方法 | 一から作成 |
| 関数名 | S3EventFunction |
| ランタイム | Python 3.12 |
| アーキテクチャ | x86_64 |
| 実行ロール | 「基本的な Lambda アクセス権限で新しいロールを作成」(デフォルトのまま) |
「関数の作成」をクリックします。
実行ロールについて:
今回の Lambda はS3イベントのメタデータ(バケット名・ファイル名・サイズ)を受け取るだけで、S3からファイルの中身を読み取る処理は行いません。
そのため、基本実行ロール(CloudWatch Logs への書き込み権限のみ)で動作します。
ファイルの中身を読み取りたい場合は、実行ロールにs3:GetObject権限を追加する必要があります。
2-2. コードの入力
関数ページ → 「コード」タブ → コードエディタで lambda_function.py を開きます。
既存の内容を全て置き換えて以下を貼り付けます。
import json
import urllib.parse
def lambda_handler(event, context):
results = []
for record in event["Records"]: # 複数ファイル同時アップロードに対応
bucket = record["s3"]["bucket"]["name"]
# キー名は URL エンコードされているためデコードが必要(日本語・スペース対応)
key = urllib.parse.unquote_plus(record["s3"]["object"]["key"])
size = record["s3"]["object"]["size"]
event_name = record["eventName"] # 例: "ObjectCreated:Put"
event_time = record["eventTime"] # 例: "2026-02-22T10:00:00.000Z"
info = {
"eventName": event_name,
"bucket": bucket,
"key": key,
"sizeBytes": size,
"eventTime": event_time,
}
print(json.dumps(info, ensure_ascii=False))
results.append(info)
print(json.dumps({"processedCount": len(results)}, ensure_ascii=False))
return {"processedCount": len(results)}「Deploy」ボタンをクリックしてコードを保存します。
③ S3 トリガーを Lambda に追加
Lambdaコンソールからトリガーを追加する方法と、S3コンソールからイベント通知を設定する方法の2つがありますが、ここではLambdaコンソールから行います。
Lambda → S3EventFunction → 「設定」タブ → 「トリガー」→「トリガーを追加」
| 設定項目 | 値 |
|---|---|
| ソース | S3 |
| バケット | ① で作成したバケット(例: my-s3-event-upload) |
| イベントタイプ | すべてのオブジェクト作成イベント |
| プレフィックス | 空欄(全フォルダが対象) |
| サフィックス | 空欄(全ファイルタイプが対象) |
| 再帰呼び出し | 「このトリガーを確認しました」にチェック |
「追加」をクリックします。
「再帰呼び出し」の警告とは:
S3バケットへのアップロードをトリガーに同じバケットへ書き込む Lambda を設定すると、無限ループが発生します。例えば「画像をアップロード → Lambdaがリサイズして同じバケットに保存 → それがまたトリガーになる → …」というパターンです。
今回の Lambda はS3への書き込みを行わず、ログを出力するだけなので問題ありません。「このトリガーを確認しました」にチェックして続行してください。
SAMとの差分: SAMの
Type: S3イベントでも同じ構成になっています。SAMは警告なしに設定されますが、同じリスクが存在します。
自動設定されること:
Lambdaのリソースベースポリシーに「S3からの呼び出しを許可するポリシー」が自動追加されます。
S3がLambdaを呼び出すためのIAM設定を手動で行う必要はありません。
④ 動作テスト
4-1. テスト用ファイルの準備
echo テストファイルです > C:\test.txt4-2. ファイルのアップロード
方法A: S3 コンソールからアップロード
S3 → my-s3-event-upload → 「アップロード」→「ファイルを追加」→ test.txt を選択 → 「アップロード」
方法B: AWS CLI でアップロード
aws s3 cp C:\test.txt s3://my-s3-event-upload/test.txt --region ap-northeast-14-3. CloudWatch Logs でログを確認
Lambda → S3EventFunction → 「モニタリング」タブ → 「CloudWatch Logs を表示」→ 最新のログストリームを開きます。
期待するログ出力:
{"eventName": "ObjectCreated:Put", "bucket": "my-s3-event-upload", "key": "test.txt", "sizeBytes": 28, "eventTime": "2026-02-22T10:00:00.000Z"}
{"processedCount": 1}確認ポイント:
"eventName": "ObjectCreated:Put"— S3の Put(アップロード)が検知されている"key": "test.txt"— アップロードしたファイル名が正しく取得されている"sizeBytes"— ファイルサイズが記録されている
4-4. 日本語ファイル名のテスト(任意)
echo 日本語テスト > "C:\テスト.txt"
aws s3 cp "C:\テスト.txt" "s3://my-s3-event-upload/テスト.txt" --region ap-northeast-1ログで "key": "テスト.txt" と正しくデコードされていることを確認します。
⑤ リソースの削除
課金を止めるために、ハンズオン完了後は必ずリソースを削除してください。
S3バケット削除の重要な注意点:
オブジェクトが残っているバケットは削除できません。
先にバケット内のファイルをすべて削除してからバケットを削除してください。SAMの
sam deleteでも同じ理由から「先にバケットを空にする」手順が必要です。この仕様はSAMを使っても変わりません。
1. S3 バケット内のオブジェクトをすべて削除する
方法A: S3 コンソールから
S3 → my-s3-event-upload → ファイルをすべて選択 → 「削除」→ 確認テキストを入力 → 「オブジェクトを削除」
方法B: AWS CLI で一括削除(推奨)
aws s3 rm s3://my-s3-event-upload --recursive --region ap-northeast-12. S3 バケットを削除する
S3 → my-s3-event-upload を選択 → 「削除」→ バケット名を入力 → 「バケットを削除」
3. Lambda 関数を削除する
Lambda → 関数 → S3EventFunction を選択 → 「アクション」→「削除」→ 確認テキストを入力 → 「削除」
4. IAM ロールを削除する(任意)
IAM → ロール → S3EventFunction-role-XXXX を選択 → 「削除」→ ロール名を入力 → 「削除」
Lambda を削除してもIAMロールはそのまま残ります。不要なら手動で削除します。
5. CloudWatch Logs のロググループを削除する(任意)
CloudWatch → ロググループ → /aws/lambda/S3EventFunction → 「アクション」→「ロググループの削除」
SAMとの対比まとめ
| SAMの記述 | コンソールでやること |
|---|---|
Type: AWS::S3::Bucket | S3 → バケットを作成 |
!Sub "${AWS::StackName}-upload-${AWS::AccountId}" | 手動でグローバルユニークなバケット名を考えて入力 |
Type: S3 イベント / Events: s3:ObjectCreated:* | Lambda → トリガーを追加(S3・全オブジェクト作成イベント) |
| リソースベースポリシー(自動) | トリガー追加時に自動設定(「再帰呼び出し」警告にチェック) |
DeletionPolicy: Delete | 手動でバケットを空にしてからバケット削除 |
sam delete | S3バケット・Lambda・IAMロール・ロググループを個別に削除 |
トラブルシューティング
| 症状 | 原因 | 対処 |
|---|---|---|
| ファイルをアップロードしてもLambdaが起動しない | S3トリガーが設定されていない | ③ の手順でトリガーを追加する |
ログに "key" が %XX%XX... と表示される | URLデコードされていない | urllib.parse.unquote_plus(key) でデコードしているか確認 |
| バケット削除がエラーになる | バケット内にオブジェクトが残っている | aws s3 rm s3://バケット名 --recursive で先に空にする |
| Lambda が 500 エラー | コードの構文エラーなど | CloudWatch Logs でエラー内容を確認 |
| トリガー追加時に「再帰呼び出し」の警告が出る | 同バケットへの書き込みトリガーへの注意 | 今回のLambdaはS3に書き込まないためチェックを入れて続行 |
まとめ
コンソール操作で確認できたこと
| SAMの記述 | コンソール操作で理解できたこと |
|---|---|
!Sub "...-${AWS::AccountId}" | バケット名のグローバルユニーク問題を手動で解決する必要がある |
Type: S3 イベント(自動) | トリガー追加画面で「再帰呼び出し警告」の意味を確認できる |
DeletionPolicy: Delete | S3は「先に空にしてから削除」が必須ルール(SAMでも同じ) |
どちらを使うべきか
| シーン | 推奨 |
|---|---|
| 本番環境・チーム開発 | SAM(バケット名の管理・再現性・速度) |
| AWSを初めて学ぶ | コンソール(各設定の意味を理解する) |
| 構築済みリソースの確認・デバッグ | コンソール(S3のオブジェクト確認・イベント通知設定の目視) |
| 同じ構成を繰り返しデプロイ | SAM(ミスゼロ・自動化) |
関連記事
- SAM版ハンズオン — コマンド数本でS3 + Lambdaトリガー環境を構築

コメント