DEV Community

Tejas Shinkar
Tejas Shinkar

Posted on

AWS Lambda Hands-On Lab

⚡ S3 → Lambda Image Resize Pipeline

An event-driven image-processing practical using Amazon S3 and AWS Lambda — verified end-to-end with CloudWatch, then deliberately broken at the IAM layer to practice diagnosing a real production-style failure.

Objective

Build a pipeline where an image uploaded to S3 automatically triggers a Lambda function, which reads the image, resizes it, and writes the generated thumbnail back to a separate S3 prefix — then validate both the working path and a deliberate failure path:

Normal operation: S3 upload → Lambda → resize → thumbnail

Failure handling: S3 upload → Lambda → AccessDenied → CloudWatch diagnosis

Architecture

                    S3 Bucket
        s3-image-resize-lambda-lab
                 /          \
                /            \
        uploads/            thumbnails/
            │                    ▲
            │ ObjectCreated      │ PutObject
            ▼                    │
        ┌────────────────────────────┐
        │          Lambda            │
        │    s3-image-processor      │
        │                            │
        │  1. Read image from S3     │
        │  2. Resize with Pillow     │
        │  3. Write thumbnail to S3  │
        └────────────────────────────┘
                     │
                     ▼
                CloudWatch Logs
Enter fullscreen mode Exit fullscreen mode

The S3 trigger was scoped to the uploads/ prefix only, and Lambda writes its output to thumbnails/ — keeping the generated thumbnail from ever re-triggering the function.

Resources created:

  • S3 bucket: s3-image-resize-lambda-lab, with prefixes uploads/ (input) and thumbnails/ (output)
  • Lambda: s3-image-processor — Python 3.12, x86_64
  • Lambda Layer: pillow-image-processing — Pillow wasn't in the base Python runtime, so it was added as a custom layer
  • Trigger: S3 ObjectCreated events on uploads/
  • Execution role: s3:GetObject on uploads/*, s3:PutObject on thumbnails/* — deliberately scoped rather than broad bucket access

Step 1 — Build the Pipeline

The S3 trigger was configured for all object-create events, scoped to the uploads/ prefix with no suffix filter (event type s3:ObjectCreated:*), which also auto-granted S3 the permission to invoke the Lambda. The execution role was given exactly two permissions — s3:GetObject on arn:aws:s3:::s3-image-resize-lambda-lab/uploads/* and s3:PutObject on arn:aws:s3:::s3-image-resize-lambda-lab/thumbnails/* — intentionally avoiding a broader S3 grant.

The handler logic follows a straightforward sequence:

Read S3 event → extract bucket + object key → get the original image from S3 → open with Pillow → resize while preserving aspect ratio → swap uploads/ for thumbnails/ in the key → put the resized image back into S3

The thumbnail target is derived directly from the source key — uploads/photo.png becomes thumbnails/photo.png.

Step 2 — Verify the Normal Workflow

An image was uploaded to uploads/. Lambda was invoked automatically, and the resized thumbnail appeared under thumbnails/ moments later. CloudWatch confirmed the full round trip:

New file uploaded: s3://s3-image-resize-lambda-lab/uploads/56291ef0-ccd4-4201-8334-1915f49fd835.png

Thumbnail created: s3://s3-image-resize-lambda-lab/thumbnails/56291ef0-ccd4-4201-8334-1915f49fd835.png
Enter fullscreen mode Exit fullscreen mode

Every stage of the path checked out: S3 upload, the S3 → Lambda trigger, Lambda execution, the image read, the resize, and the thumbnail write.

Step 3 — Break It on Purpose, Then Diagnose

To simulate a real permissions failure, the s3:GetObject statement for uploads/* was removed from the Lambda execution role (leaving s3:PutObject on thumbnails/* untouched), and a new image was uploaded to uploads/.

The S3 event still fired and Lambda still ran — it just failed partway through. CloudWatch isolated exactly where:

[ERROR] AccessDenied
An error occurred (AccessDenied) when calling the GetObject operation:
User: arn:aws:sts::...:assumed-role/s3-image-processor-role-.../s3-image-processor
is not authorized to perform: s3:GetObject
Enter fullscreen mode Exit fullscreen mode

This pinned the failure precisely on the execution role's permissions, not the S3 trigger, the Lambda Layer, or the runtime — the trigger fired correctly and the function only failed once its code tried to read the source image. The original s3:GetObject grant was then restored and the successful flow re-verified before cleaning up the lab environment.

The event path worked; the execution permission did not.

Troubleshooting Pattern

The sequence that actually isolated the fault, worth reusing on any event-driven pipeline:

Did the upload happen? → did S3 invoke Lambda? → did Lambda start? → where did Lambda fail? → check the CloudWatch error → check the execution-role permission → restore only the required permission

This is more useful than simply checking whether the final thumbnail exists, because it separates an event/configuration problem from an execution/permission problem instead of treating "nothing happened" as one undifferentiated failure.

Key Takeaways

  • Separate input and output prefixes (uploads/ vs thumbnails/) stop the pipeline's own output from re-triggering itself — for larger systems, separate input/output buckets give an even stronger isolation boundary.
  • Scope Lambda permissions to the exact paths needed (GetObject on uploads/*, PutObject on thumbnails/*) rather than granting broad, bucket-wide S3 access.
  • CloudWatch is the first place to look when an event-driven pipeline fails — a successful S3 upload says nothing about whether downstream processing succeeded, since the failure can occur well after the event reaches Lambda.
  • Event-driven systems fail at multiple independent boundaries, and a production troubleshooting process should identify which boundary failed before changing anything:

S3 upload → S3 event → Lambda invocation → IAM authorization → S3 read → image processing → S3 write

Final Result

Normal path: image upload → S3 /uploads/ObjectCreated event → Lambda → Pillow resize → S3 /thumbnails/ → thumbnail ✅

Failure path: image upload → S3 /uploads/ObjectCreated event → Lambda → GetObjectAccessDenied ❌ → CloudWatch identifies the missing permission

Outcome: successfully built and verified an S3-triggered Lambda image-processing pipeline end to end, then deliberately broke and correctly diagnosed an IAM permission failure using CloudWatch logs.

Top comments (0)