AWSハンズオン - S3へのファイルアップロードをトリガーにLambdaを自動起動しよう【SAM版 / Windows対応】

AWS Basic
スポンサーリンク
スポンサーリンク

はじめに

「S3にファイルが上がったら自動で処理したい」というのは、AWSを使う現場でよく出てくる要件です。画像のリサイズ、CSVの集計、ログファイルの解析など、ファイルアップロードを起点にした自動処理はサーバーレス構成の代表的なユースケースです。

この記事では、S3 + Lambda によるファイルアップロードトリガー処理を、AWS SAM を使ってゼロから構築するハンズオンを紹介します。

ファイルアップロード(aws s3 cp)
  ↓ S3 ObjectCreated イベント発火
S3 バケット(UploadBucket)
  ↓ Lambda を呼び出し
Lambda(Python 3.12 / S3EventFunction)
  ↓ ファイル情報(バケット名・ファイル名・サイズ)をログ出力
CloudWatch Logs

このハンズオンで体験できること:

  • S3 ObjectCreated イベントによる Lambda の自動起動
  • URLデコード処理(日本語ファイル名・特殊文字を含むキー名の正しい取得)
  • SAMテンプレートで S3 バケット名をグローバルユニークにする !Sub の使い方
  • DeletionPolicy と「バケットを先に空にしてから削除」というSAM特有の注意点

この記事の特徴:

  • Windows(VSCode + コマンドプロンプト)での実際の手順を詳細に解説
  • フォルダ付きキー・日本語ファイル名など実務でよく出る応用テストも含む
  • sam delete で失敗しやすいポイントと対処法を詳細に解説

Amazon検索[本 AWS 開発]

スポンサーリンク

S3イベントの仕組みを理解する

キーワード解説

用語意味
S3イベント通知S3バケットで特定の操作(アップロードなど)が行われたときに他サービスを呼び出す仕組み
ObjectCreatedファイルのアップロード・コピー・マルチパート完了などの「作成」系イベントの総称
リソースベースポリシーLambda側に「S3からの呼び出しを許可する」ポリシーを設定する仕組み
URLデコードS3のキー名は特殊文字がURLエンコードされるため、Lambda側でデコードして読み取る処理

S3イベントの種類

SAMテンプレートで指定できる代表的なイベント:

イベント発火タイミング
s3:ObjectCreated:PutPUTアップロード(aws s3 cp など)
s3:ObjectCreated:PostHTMLフォームからのアップロード
s3:ObjectCreated:Copyオブジェクトのコピー
s3:ObjectCreated:*上記すべての作成系イベント(今回はこれを使用)
s3:ObjectRemoved:*オブジェクトの削除イベント

今回は s3:ObjectCreated:* を使い、アップロード・コピーを問わず全ての作成操作でLambdaを起動します。


スポンサーリンク

前提条件

ツール確認コマンド最低バージョン目安
AWS CLI v2aws --version2.x
AWS SAM CLIsam --version1.x
Pythonpython --version3.12推奨
Gitgit --version-
VSCode-最新版推奨
aws sts get-caller-identity

アカウントIDが表示されれば認証設定済みです。


使用するAWSサービス

サービス役割料金
S3(標準ストレージ)ファイルの保存・イベント発火5GBまで無料(12ヶ月)
Lambdaファイル情報のログ記録100万リクエスト無料枠あり
CloudWatch LogsLambda実行ログの保存5GBまで無料
S3(デプロイ用)SAMのデプロイパッケージ置き場自動作成・少量なのでほぼ無料

学習目的の短時間ハンズオンであれば、ほぼ無料枠内で収まります。


Step 1: プロジェクトフォルダを開く

cd C:\my-aws\aws-learning-projects\s3-event-lambda

フォルダ構造

s3-event-lambda/
├── template.yaml       # SAMテンプレート(S3バケット + Lambda定義)
├── samconfig.toml      # デプロイ設定(gitignore対象・毎回手動作成が必要)
├── docs/
│   ├── 1_console.md    # AWSコンソール版手順
│   └── 2_sam.md        # SAM版手順
└── src/
    └── app.py          # Lambda関数(ファイル情報をCloudWatch Logsに出力)

Step 2: SAMテンプレートの確認(template.yaml)

template.yaml がAWSリソース全体の設計図です。S3バケットとLambda関数をこの1ファイルで定義します。

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: S3 file upload trigger with Lambda

Globals:
  Function:
    Runtime: python3.12
    Timeout: 30
    MemorySize: 128

Resources:
  UploadBucket:
    Type: AWS::S3::Bucket
    Properties:
      # アカウントIDを含めることでグローバルユニークを保証
      BucketName: !Sub "${AWS::StackName}-upload-${AWS::AccountId}"
    DeletionPolicy: Delete   # sam delete 時にバケットも削除(空の場合のみ)

  # Lambda 関数
  S3EventFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: src/
      Handler: app.lambda_handler
      Description: S3アップロードイベントを受信してファイル情報をログ出力する
      Events:
        S3Event:
          Type: S3                          # S3イベントトリガーを設定
          Properties:
            Bucket: !Ref UploadBucket       # 上で作ったバケットを参照
            Events: s3:ObjectCreated:*      # 全ての「作成」イベントをトリガー

Outputs:
  BucketName:
    Description: "アップロード先バケット名"
    Value: !Ref UploadBucket
  S3EventFunctionArn:
    Description: "Lambda 関数 ARN"
    Value: !GetAtt S3EventFunction.Arn
  CloudWatchLogsGroup:
    Description: "CloudWatch Logs グループ名"
    Value: !Sub "/aws/lambda/${S3EventFunction}"

template.yaml のポイント解説

BucketName: !Sub "${AWS::StackName}-upload-${AWS::AccountId}"
S3バケット名はAWS全体(全リージョン・全アカウント)でグローバルユニークである必要があります。スタック名+アカウントIDを組み合わせることで、他のアカウントと名前が被る可能性を排除しています。コンソール版では手動で一意な名前を考える必要があります。

Type: S3 イベント
SAMがS3バケットのイベント通知設定とLambdaのリソースベースポリシー(「S3からの呼び出しを許可する」設定)を自動作成します。コンソール版では「Lambda → トリガーを追加」の画面操作が必要です。

DeletionPolicy: Delete
sam delete 実行時にS3バケットを削除しようとします。ただし、オブジェクトが残っているバケットは削除できないため、sam delete 前に必ずバケットを空にする必要があります(Step 9で詳しく解説)。

S3バケット名についての補足:
一度使われたバケット名は削除後しばらく再利用できないことがあります。
アカウントIDを含める方法が最も確実です。


Step 3: Lambdaコードの確認(src/app.py)

src/app.py がファイル情報取得の本体です。S3イベントからバケット名・ファイル名・サイズ・イベント種別を取得してCloudWatch Logsに記録します。

import json
import urllib.parse


def lambda_handler(event, context):
    results = []

    for record in event["Records"]:          # 複数ファイル同時アップロードに対応
        # S3 イベントからファイル情報を取得
        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)}

app.py のポイント解説

event["Records"] のループ処理
S3イベントはリスト形式で渡されます。通常は1件ですが、複数ファイルの同時アップロード時に複数件になる場合があるため、for ループで処理します。

urllib.parse.unquote_plus(key) が必要な理由
S3のキー名(ファイルパス)に日本語や特殊文字・スペースが含まれると、URLエンコードされてLambdaに渡されます。

アップロードしたファイル名デコード前(Lambdaに渡される値)デコード後
テスト.txt%E3%83%86%E3%82%B9%E3%83%88.txtテスト.txt
my file.txtmy+file.txtmy file.txt
report-2026.csvreport-2026.csv(変化なし)report-2026.csv

urllib.parse.unquote_plus はこのデコードを正しく処理します。

外部ライブラリ不使用
標準ライブラリ(json, urllib.parse)のみ使用するため、requirements.txt が不要です。


Step 4: samconfig.toml を作成する

samconfig.toml.gitignore で管理外のため、毎回手動で作成する必要があります。

s3-event-lambda/samconfig.toml を新規作成して以下を貼り付けてください。

version = 0.1

[default.deploy.parameters]
stack_name = "s3-event-lambda-stack"
resolve_s3 = true
s3_prefix = "s3-event-lambda-stack"
region = "ap-northeast-1"
confirm_changeset = true
capabilities = "CAPABILITY_IAM"
disable_rollback = true
image_repositories = []

[default.global.parameters]
region = "ap-northeast-1"

【注意】samconfig.toml の置き場所

s3-event-lambda/ フォルダの直下に置く必要があります。


Step 5: sam build(ビルド)

cd C:\my-aws\aws-learning-projects\s3-event-lambda
sam build
Build Succeeded

Built Artifacts  : .aws-sam/build
Built Template   : .aws-sam/build/template.yaml

Step 6: sam deploy(デプロイ)

sam deploy
Deploy this changeset? [y/N]: y

y を入力して進めます。数分でデプロイが完了します。

デプロイ完了の確認

CloudFormation outputs from deployed stack
----------------------------------------------------------------------
Outputs
----------------------------------------------------------------------
Key    BucketName
Value  s3-event-lambda-stack-upload-123456789012

Key    S3EventFunctionArn
Value  arn:aws:lambda:ap-northeast-1:123456789012:function:s3-event-lambda-stack-S3EventFunction-XXXX

Key    CloudWatchLogsGroup
Value  /aws/lambda/s3-event-lambda-stack-S3EventFunction-XXXX
----------------------------------------------------------------------

BucketName を控えておきます。(テスト時に使います)

BucketAlreadyExists エラーが出た場合:
既に同名バケットが他アカウントに存在しています。samconfig.tomlstack_name を別の名前に変更して再デプロイしてください。


Step 7: 動作テスト

7-1. テスト用ファイルの準備

echo テストファイルです > C:\test.txt

7-2. バケット名を変数にセットする

set BUCKET=s3-event-lambda-stack-upload-123456789012

BUCKET の値は Outputs の BucketName に表示された実際の値に変更してください)

7-3. ファイルのアップロード

aws s3 cp C:\test.txt s3://%BUCKET%/test.txt --region ap-northeast-1

7-4. CloudWatch Logs でログを確認

aws logs tail /aws/lambda/s3-event-lambda-stack-S3EventFunction-XXXX --follow

期待するログ出力:

{"eventName": "ObjectCreated:Put", "bucket": "s3-event-lambda-stack-upload-123456789012", "key": "test.txt", "sizeBytes": 28, "eventTime": "2026-02-22T10:00:00.000Z"}
{"processedCount": 1}

確認ポイント:

  • "eventName": "ObjectCreated:Put" — S3 の Put(アップロード)が検知されている
  • "key": "test.txt" — アップロードしたファイル名が正しく取得されている
  • "sizeBytes" — ファイルサイズ(バイト)が記録されている

7-5. フォルダ付きキーのテスト(任意)

S3の「フォルダ」はキー名のプレフィックスとして表現されます。

aws s3 cp C:\test.txt s3://%BUCKET%/images/photo.jpg --region ap-northeast-1

ログで "key": "images/photo.jpg" となることを確認します。S3にフォルダ構造でファイルを整理している場合でも、キー名全体(フォルダ名/ファイル名)が取得できます。

7-6. 日本語ファイル名のテスト(任意)

echo 日本語テスト > "C:\テスト.txt"
aws s3 cp "C:\テスト.txt" "s3://%BUCKET%/テスト.txt" --region ap-northeast-1

ログで "key": "テスト.txt" と正しくデコードされていることを確認します。urllib.parse.unquote_plus がなければ %E3%83%86%E3%82%B9%E3%83%88.txt のままになります。


Step 8: AWSコンソールで確認(任意)

  • S3: S3 → s3-event-lambda-stack-upload-XXXX → アップロードしたファイルが存在することを確認
  • Lambda: Lambda → 関数 → s3-event-lambda-stack-S3EventFunction-XXXX → 「設定」→「トリガー」でS3トリガーを確認
  • CloudWatch Logs: Lambda → 対象関数 → 「モニタリング」タブ → 「CloudWatch Logs を表示」


Step 9: リソースの削除

課金を止めるために、ハンズオン完了後は必ずリソースを削除してください。

重要: sam delete の前に必ずバケットを空にしてください。
S3バケットにオブジェクトが残った状態で sam delete を実行すると、CloudFormation のスタック削除が失敗します。

① バケット内のオブジェクトをすべて削除する

aws s3 rm s3://%BUCKET% --recursive --region ap-northeast-1

② スタックを削除する

sam delete --stack-name s3-event-lambda-stack --region ap-northeast-1
Are you sure you want to delete the stack s3-event-lambda-stack? [y/N]: y
Are you sure you want to delete the folder s3-event-lambda-stack in S3? [y/N]: y

削除完了の確認

aws cloudformation describe-stacks --stack-name s3-event-lambda-stack --region ap-northeast-1
An error occurred (ValidationError): Stack with id s3-event-lambda-stack does not exist

バケット削除を忘れて sam delete が失敗した場合

Error: Failed to delete the stack: s3-event-lambda-stack, ...
The following resource(s) failed to delete: [UploadBucket]

このエラーが出た場合は、バケットを空にしてから再度 sam delete を実行します。

aws s3 rm s3://%BUCKET% --recursive --region ap-northeast-1
sam delete --stack-name s3-event-lambda-stack --region ap-northeast-1

トラブルシューティング

症状原因対処
sam deployBucketAlreadyExists エラー同名バケットが他アカウントに存在するsamconfig.tomlstack_name を変更する
sam deployMissing --stack-name エラーsamconfig.toml がないs3-event-lambda/ 直下に samconfig.toml を作成する
ファイルアップロード後もログが出ないLambda が呼ばれていないLambda → 「設定」→「トリガー」でS3トリガーが有効か確認
ログの "key"%XX%XX... のままURLデコードされていないapp.pyunquote_plus 処理を確認
sam delete が失敗するバケットにオブジェクトが残っているaws s3 rm s3://バケット名 --recursive で先に空にする

まとめ

今回のハンズオンで実現したこと:

確認項目内容
S3イベントトリガーアップロード直後にLambdaが自動起動することを確認
URLデコード日本語ファイル名が正しく取得できることを確認
フォルダ付きキーimages/photo.jpg のようなプレフィックス付きキーも取得できることを確認
削除バケットを先に空にしてから sam delete でクリーンアップ

SAMのメリットを実感できたポイント

  • !Sub "${AWS::StackName}-upload-${AWS::AccountId}" でバケット名のグローバルユニークを自動保証(コンソール版では手動で一意な名前を考える必要がある)
  • Type: S3 の1行でS3イベント通知設定とLambdaリソースベースポリシーを自動作成(コンソール版では「トリガーを追加」の手動操作が必要)

このハンズオンの発展形

  • S3からファイルの中身を読み取る — Lambda の実行ロールに s3:GetObject 権限を追加して、CSVやJSONを読み込む処理に拡張
  • DynamoDBへの記録 — ファイル情報をDynamoDBに書き込んで一覧管理
  • 特定のフォルダ・拡張子のみトリガーPrefix: images/Suffix: .csv でフィルタリング
  • 画像リサイズPillow ライブラリと組み合わせてアップロード時に自動リサイズ

コンソール操作と比較してみる

SAMが裏で何をやっているか、全く同じ構成をAWSコンソールのみで構築する手順をまとめました。「バケット名のグローバルユニーク問題」や「再帰呼び出しの警告」など、コンソールならではのつまずきポイントが体験できます。

  • AWSコンソールだけでS3 + Lambdaファイルアップロードトリガーを構築する手順【SAM版との比較付き】


関連記事

Amazon検索[本 AWS 開発]

コメント