Trigger Lambda with S3 Events
Learn how to trigger AWS Lambda functions automatically when objects are uploaded to S3. Step-by-step guide with hands-on examples, troubleshooting tips, and next steps.
Focus: trigger lambda with s3 events
Ever stared at your S3 bucket waiting for a new file to appear, then manually kicked off a script to process it? That's exactly the kind of friction that eats your day. In this lesson, you'll eliminate that manual step by wiring S3 to automatically fire a Lambda function the instant an object lands — the event-driven backbone of countless serverless Python backends.
The Problem This Lesson Solves
Think about any workflow that depends on files: uploading CSVs to a data lake, pushing images to a media pipeline, or dumping logs for analysis. Without automation, you have two choices: a developer babysits the bucket and manually triggers processing, or you write a polling script that checks S3 every few seconds and burns compute (and your patience) for nothing.
Both approaches fail at scale. Manual work doesn't ship. Polling adds latency and needless cost. What you really want is the event to be the trigger — the moment an object appears, the work starts. That's exactly what S3 event notifications to Lambda give you.
By the end of this lesson, you'll not only understand the architecture, but you'll have a working, testable setup that you can drop into any Python backend. We'll build on the S3 and Lambda concepts from earlier lessons in this track, so if you haven't created a bucket or a function yet, go back and do that first — this lesson assumes you can.
Core Concept / Mental Model
Let's build a picture you can carry in your head.
The Event-Driven Iceberg
Imagine an iceberg: the only part you see is the trigger — the S3 upload. But underneath, a whole chain reacts: S3 emits a notification, a notification destination (like Lambda) receives it, the function runs your Python code, and the result is your processed output.
Here are the three essential pieces:
- Event source: The S3 bucket. When a
PUTrequest succeeds and an object is created, S3 can emit an event. - Event notification: A configuration on the bucket that says, "When this happens, send a message to that destination." For Lambda, this is a special resource-based policy.
- Event handler: Your Lambda function. It receives a JSON payload describing the event and runs your code.
Pro tip: The single most important mental shift is to stop thinking "poll" and start thinking "react." Your code only runs when something meaningful happens — zero waste.
The Invocation Model
When an object is uploaded, S3 sends a test event (for console setups) or a real event. Lambda synchronously or asynchronously invokes your function, passing a payload like this:
{
"Records": [
{
"eventName": "ObjectCreated:Put",
"s3": {
"bucket": {
"name": "my-bucket"
},
"object": {
"key": "uploads/report.csv"
}
}
}
]
}
Your function reads event['Records'][0]['s3']['object']['key'] to know which file triggered it. That's the whole contract.
How It Works Step by Step
Let's walk the exact sequence from upload to function execution — know this cold and every config will make sense.
- An object is created — A
PUT,POST, or multipart upload completes in your S3 bucket. - S3 evaluates its notification configuration — The bucket has a list of event types (e.g.,
s3:ObjectCreated:*) and destinations. - S3 publishes the event — It writes an event message and delivers it to the configured destination. For Lambda, this is done via an internal topic that Lambda subscribes to.
- Lambda invokes your function — The service sees the new message, matches it to your function, and runs it. The function receives the event JSON in the
eventparameter. - Your code executes — You process the object, maybe download it with
boto3, transform, and store results elsewhere. - You can log and monitor — CloudWatch Logs captures your function's output; metrics show invocations and errors.
The cause-and-effect chain is direct: upload triggers event triggers invocation triggers code. There's no queue, no scheduled job, no hidden timer.
Pro tip: S3 event notifications are at-least-once — your function might be invoked more than once for the same object. Make your code idempotent (e.g., check if a processed file already exists).
Hands-On Walkthrough
Time to build it. We'll create a bucket, a Lambda function, a trigger, and test the whole loop. You'll need AWS CLI and Python 3.10+ (or the Lambda console — I'll show both).
1. Create a Bucket
aws s3 mb s3://my-event-demo-bucket-$RANDOM
Note the bucket name — S3 bucket names are globally unique. I'll use BUCKET_NAME as a placeholder.
2. Write Your Lambda Function
Create a file lambda_function.py with a simple handler that logs the event and, if the object is a .csv, prints a message:
import json
import urllib.parse
def lambda_handler(event, context):
print("Received event: " + json.dumps(event, indent=2))
# Get the bucket and object key from the event
bucket = event['Records'][0]['s3']['bucket']['name']
key = urllib.parse.unquote_plus(event['Records'][0]['s3']['object']['key'], encoding='utf-8')
if key.endswith('.csv'):
print(f"CSV detected: s3://{bucket}/{key}")
else:
print(f"Non-CSV file: {key} — ignoring")
return {
'statusCode': 200,
'body': json.dumps(f'Processed {key}')
}
3. Create the Lambda Function via CLI
Package your code and create the function:
zip function.zip lambda_function.py
aws lambda create-function \
--function-name s3-trigger-demo \
--runtime python3.11 \
--role arn:aws:iam::YOUR_ACCOUNT_ID:role/lambda-s3-role \
--handler lambda_function.lambda_handler \
--zip-file fileb://function.zip
You'll need an IAM role with basic Lambda execution permissions (CloudWatch Logs) — see earlier lessons.
4. Add the Trigger
Now wire S3 to Lambda. Grant S3 permission to invoke your function, then add the notification config:
aws lambda add-permission \
--function-name s3-trigger-demo \
--statement-id s3invoke \
--action "lambda:InvokeFunction" \
--principal s3.amazonaws.com \
--source-arn arn:aws:s3:::BUCKET_NAME
aws s3api put-bucket-notification-configuration \
--bucket BUCKET_NAME \
--notification-configuration '{
"LambdaFunctionConfigurations": [
{
"LambdaFunctionArn": "arn:aws:lambda:REGION:ACCOUNT_ID:function:s3-trigger-demo",
"Events": ["s3:ObjectCreated:*"]
}
]
}'
Pro tip: If you use the AWS console, the UI does steps 4 and 5 for you in the bucket's Properties → Event notifications panel. But knowing the CLI makes it scriptable.
5. Test the End-to-End Flow
Upload a file and watch the logs:
aws s3 cp test.csv s3://BUCKET_NAME/
aws logs tail /aws/lambda/s3-trigger-demo --follow
Expected output (CloudWatch Logs):
Received event: {"Records": [{"eventName": "ObjectCreated:Put", ...}]}
CSV detected: s3://BUCKET_NAME/test.csv
You just triggered a Lambda with an S3 event. 🎉
Compare Options / When to Choose What
Not every automation needs S3 → Lambda. Here's how it stacks up against alternatives:
| Trigger Type | Latency | Use Case | Complexity |
|---|---|---|---|
| S3 → Lambda (this lesson) | Near-real-time (sub-second) | Event-driven processing: resizing images, validating CSVs, fanning out to queues | Low — native integration |
| S3 → SNS → Lambda | Similar | Fan-out to multiple subscribers (email, SMS, Slack) | Medium — extra hop |
| S3 → SQS → Lambda | Polling delays (1–30s) | High-throughput, when you want a durable queue and retries | Medium — requires queue polling |
| Polling script (not event-driven) | Second-to-minutes depending frequency | Prototypes, tiny volumes | Low, but manual and wasteful |
When to choose what: - Use S3 → Lambda directly when you have a single consumer workflow and want minimal moving parts. - Use S3 → SNS when the same event must trigger multiple actions (e.g., log, alert, process). - Use S3 → SQS when you need a buffer before processing, or if your function might fail and you want to retry later without losing messages.
For this lesson, stick with the direct trigger — it's the cleanest.
Troubleshooting & Edge Cases
Even simple setups break. Here's the fix-cheat-sheet.
"Access Denied" when S3 tries to invoke Lambda
- Symptom: In the console, the trigger shows "not configured" or invocations fail with
AccessDeniedException. - Fix: You forgot the resource-based policy (
add-permission). Double-check thesource-arnmatches your bucket ARN exactly.
"The provided execution role does not have permissions to call the put-object" — wait, you don't use that
Remember your function needs to read from S3 to process the file. Even if the trigger works, downloading the object fails if the role lacks s3:GetObject.
- Fix: Attach a policy allowing
s3:GetObjecton that bucket.
Pro tip: Test your function with a manual payload (from the console's Test tab) before uploading a real file — it isolates trigger issues from code bugs.
Lambda never fires
- Verify the event type matches. If you upload via multipart upload, the event is
ObjectCreated:CompleteMultipartUpload— a prefix filter likeObjectCreated:*catches it. - Check the bucket's notification configuration is attached to the right bucket (easy to mix up if you copied a name).
- Inspect CloudWatch Logs — if the function isn't invoked, the trigger config is the problem; if there's an error, fix your code.
Duplicate executions
S3 is at-least-once, so retries can double-invoke. Design your handler to be idempotent — e.g., check a database or S3 location for existing processed output before doing work.
What You Learned & What's Next
You've mastered the fundamental pattern: how to trigger a Lambda function from an S3 event, step by step. You can now:
- Explain the event-driven model and the payload contract
- Configure a bucket notification and attach a Lambda trigger via CLI or console
- Write a Python handler that reads
Recordsand processes object keys - Troubleshoot the most common issues (permissions, event types, idempotency)
This is the core of every serverless pipeline. Next up in the AWS Tutorial track, you'll extend this pattern to fan out with SNS or SQS — imagine your S3 upload triggering an email report to 50 users plus a database update. That's where the real world beckons.
Go build something that reacts, not polls.
Practice recap
Test your understanding: modify the demo function to print the object's size from the event, then upload a file and verify the output in CloudWatch Logs. Next, add a prefix filter so only .png in uploads/ triggers the function — no code change required, just the notification config.
Common mistakes
- Forgetting to add the resource-based permission for S3 to invoke Lambda — without it, S3 silently drops events.
- Using an overly narrow event filter that misses multipart uploads — use
s3:ObjectCreated:*unless you have a specific reason. - Not making the Lambda handler idempotent — duplicate invocations on retries can process the same file twice.
- Forgetting to give your Lambda role
s3:GetObjectpermissions — the trigger fires, but the function can't read the file.
Variations
- Trigger Lambda via S3 → SNS when you need to fan out to multiple consumers (e.g., Slack alerts + processing).
- Use S3 → SQS → Lambda for a decoupled, queued architecture with retry and batch processing.
- Limit the trigger to a specific prefix (e.g.,
uploads/) to reduce noise and cost by only reacting to relevant objects.
Real-world use cases
- Automatically resize and compress images uploaded to a user-content bucket, storing optimized versions.
- Validate and parse CSV files dumped from a data pipeline, inserting rows into DynamoDB for reporting.
- Trigger a thumbnail generation workflow when new media hits a shared marketing bucket, notifying admins when complete.
Key takeaways
- S3 event notifications let you trigger Lambda directly with near-zero latency — no polling.
- The Lambda handler receives an event with
Records[]; you extract bucket and key from there. - You must configure both a resource-based policy (allow S3 to invoke) and a bucket notification config.
- Classic pitfalls are permission gaps (on the bucket and the IAM role) and event-type filtering.
- Design for at-least-once semantics — make your processing idempotent.
Keep learning
Related tutorials, quizzes, and articles for this topic.
Discussion
Questions, corrections, and tips help everyone reading this page.
0 comments
Add a comment
No comments yet — start the thread.