Skip to main content
Skip to main content
Still in beta — questions, comments or suggestions? aramb@aramb.dev

Build a Simple Serverless Upload Flow with S3 + Lambda

Hands-on lab: upload a file to S3, trigger a Lambda function, read the event, and verify the result in CloudWatch Logs.

25 min
Introductory
AWS Free TierFREE TIER

All services used in this lesson are covered by the AWS Free Tier.

AWS Services Used

S312-month free tierLambda1M requests/month always freeCloudWatch Logs5 GB ingestion/month always free

What you are building

Amazon S3 can publish event notifications when things happen in a bucket, and one supported destination is AWS Lambda. A common example is an ObjectCreated event that invokes a Lambda function when a file is uploaded. The Lambda function must be in the same Region as the S3 bucket.

S3 invokes the function asynchronously and passes a JSON event that includes details such as the bucket name and object key.


What you will build

The serverless upload flow: S3 event triggers Lambda
  • One S3 bucket for uploads
  • One Lambda function
  • One S3 trigger for ObjectCreated events
  • One safe prefix like incoming/
  • One CloudWatch Logs view to confirm the function ran

Safety rule before you start

Warning

If your Lambda function writes back to the same bucket that triggers it, you can accidentally create a recursive loop. Use two buckets or restrict the trigger to a specific incoming prefix and never write back into that prefix.

For this lab, use a trigger prefix of incoming/ and only upload files into that prefix. Do not have the function write back into incoming/.

Why the incoming/ prefix matters for loop prevention

Part 1: Create the S3 bucket

Create a general purpose bucket in the Region you want to use. Make sure you remember the Region, because the Lambda function must be created in that same Region.

A good name pattern: yourname-serverless-upload-12345

Leave the bucket private. This lab does not need public website access.


Part 2: Create the Lambda function

Create a Lambda function in the same Region as the bucket.

For this first version, use simple Python code like this:

import json
import urllib.parse

def lambda_handler(event, context):
    records = event.get("Records", [])

    for record in records:
        bucket = record["s3"]["bucket"]["name"]
        key = urllib.parse.unquote_plus(
            record["s3"]["object"]["key"], encoding="utf-8"
        )
        event_name = record["eventName"]

        print(f"Event: {event_name}")
        print(f"Bucket: {bucket}")
        print(f"Key: {key}")

    return {
        "statusCode": 200,
        "body": json.dumps({"processed_records": len(records)})
    }

This code reads the S3 event data and logs it so you can verify the end-to-end flow.


Part 3: Use the right execution role

If your function only logs the event, the basic Lambda logging permissions are enough. If your function later needs to read the uploaded object from S3, then it also needs s3:GetObject permission in its execution role.

Tip

For a later upgrade, move bucket names or operational settings into environment variables instead of hard-coding them.


Part 4: Add the S3 trigger

Open the Lambda function and add an S3 trigger. Choose:

  • Your bucket
  • Event type: ObjectCreated
  • Prefix: incoming/

This prefix matters. It means only uploads whose keys begin with incoming/ will trigger the function, which is exactly the loop-avoidance pattern.


Part 5: Upload a test file

Upload a file into the bucket using a key like:

incoming/hello.txt

You can put a small line inside the file:

Hello from my first serverless upload flow.

Part 6: Verify the result in CloudWatch Logs

After the upload, open the Lambda function's monitoring area or CloudWatch Logs and inspect the latest log stream.

You should see log lines similar to:

  • The event type
  • The bucket name
  • The object key

That confirms:

  1. The S3 event notification fired
  2. Lambda received the event
  3. Your function parsed the event correctly

Verify your work

StepSuccess condition
Create bucketBucket exists in the same Region you will use for Lambda
Create Lambda functionFunction exists and can write logs
Add S3 triggerTrigger listens for ObjectCreated on incoming/
Upload test fileincoming/hello.txt is uploaded
Check logsYou see the bucket name and key in CloudWatch Logs
Avoid recursionFunction does not write back into the triggering prefix

Micro-activity 1: Event reading

Think about it

After your upload, reflect: What bucket name did Lambda log? What object key did Lambda log? What event name did Lambda log? Did the upload happen inside the trigger prefix?


Micro-activity 2: Prevent the loop

Think about it

Why is writing back to the same triggering bucket risky? What is one safe way to avoid that problem? Think about what happens when a Lambda function writes an object that re-triggers itself.


Summary

In this lesson, you built a basic event-driven AWS workflow: an S3 upload created an event notification, Lambda received the event asynchronously, and the result was visible in CloudWatch Logs. This is one of the cleanest examples of serverless architecture because it shows storage, events, compute, and observability working together.

You also learned the most important safety rule for S3-triggered Lambda functions: avoid recursion by using two buckets or by limiting the trigger to an incoming prefix.


Quiz

Knowledge Check
1 / 8

What can Amazon S3 use to invoke a Lambda function?