S3 Event Notifications can trigger Lambda functions on object uploads, but not in the way most people initially assume.

Let’s see it in action. Imagine we have an S3 bucket named my-cool-bucket-for-events. When a new object, say images/photo.jpg, is uploaded to it, we want our Lambda function, ImageProcessorFunction, to automatically execute.

Here’s how the configuration looks on the S3 side. We’ll use the AWS CLI for this, as it’s often the most direct way to manage these settings.

First, we need to define the event notification itself. This involves specifying the bucket, the event type (in this case, s3:ObjectCreated:* which covers all object creation events like PUT, POST, COPY, and Multipart Upload completion), the destination (our Lambda function’s ARN), and the format of the notification payload.

aws s3api put-bucket-notification-configuration --bucket my-cool-bucket-for-events --notification-configuration '{
    "LambdaFunctionConfigurations": [
        {
            "Id": "MyLambdaTriggerConfig",
            "LambdaFunctionArn": "arn:aws:lambda:us-east-1:123456789012:function:ImageProcessorFunction",
            "Events": [
                "s3:ObjectCreated:*"
            ],
            "Filter": {
                "Key": {
                    "FilterRules": [
                        {
                            "Name": "suffix",
                            "Value": ".jpg"
                        }
                    ]
                }
            }
        }
    ]
}'

Notice the Filter section. This is crucial. Without it, every object creation in the bucket would trigger the Lambda. Here, we’ve configured it to only trigger for objects ending with .jpg. You can filter by prefix, suffix, or both.

The Lambda function ImageProcessorFunction itself needs to be configured to accept the S3 event payload. When S3 triggers a Lambda function, it sends a JSON payload containing details about the event.

A typical event payload looks like this:

{
  "Records": [
    {
      "eventVersion": "2.1",
      "eventSource": "aws:s3",
      "awsRegion": "us-east-1",
      "eventTime": "2023-10-27T10:00:00.000Z",
      "eventName": "ObjectCreated:Put",
      "userIdentity": {
        "principalId": "EXAMPLE"
      },
      "requestParameters": {
        "sourceIPAddress": "127.0.0.1"
      },
      "responseElements": {
        "x-amz-request-id": "EXAMPLE123456789",
        "x-amz-id-2": "EXAMPLE123456789"
      },
      "s3": {
        "s3SchemaVersion": "1.0",
        "configurationId": "MyLambdaTriggerConfig",
        "bucket": {
          "name": "my-cool-bucket-for-events",
          "ownerIdentity": {
            "principalId": "EXAMPLE"
          },
          "arn": "arn:aws:s3:::my-cool-bucket-for-events"
        },
        "object": {
          "key": "images/photo.jpg",
          "size": 1024,
          "eTag": "0123456789abcdef0123456789abcdef",
          "sequencer": "0A1B2C3D4E5F678901"
        }
      }
    }
  ]
}

Your Lambda function code would then parse this event object. For example, in Python:

import json
import boto3

s3_client = boto3.client('s3')

def lambda_handler(event, context):
    print("Received event: " + json.dumps(event, indent=2))

    for record in event['Records']:
        bucket_name = record['s3']['bucket']['name']
        object_key = record['s3']['object']['key']

        print(f"Processing object {object_key} from bucket {bucket_name}")

        # Here you would add your image processing logic
        # e.g., download the object, resize it, upload a thumbnail, etc.
        try:
            response = s3_client.get_object(Bucket=bucket_name, Key=object_key)
            object_content = response['Body'].read()
            print(f"Successfully downloaded {object_key}, size: {len(object_content)} bytes")
            # Further processing...
        except Exception as e:
            print(f"Error processing object {object_key}: {e}")
            raise e

    return {
        'statusCode': 200,
        'body': json.dumps('Successfully processed S3 event!')
    }

The key lever you control is the Filter configuration within the put-bucket-notification-configuration API call. This allows you to be very precise about which events trigger your Lambda, preventing unnecessary invocations and keeping costs down. You can specify multiple LambdaFunctionConfigurations for the same bucket, each with different events or filters, allowing a single bucket to trigger multiple different Lambda functions based on object characteristics.

The most surprising aspect is how the actual trigger mechanism works under the hood. S3 doesn’t "push" events to Lambda directly in the way you might think of a traditional message queue. Instead, when an event occurs, S3 publishes that event to an internal AWS service that acts as an event bus. Lambda is then subscribed to this event bus, and when a matching notification configuration is found, Lambda polls this bus for new events to process. This polling mechanism means there’s a slight, albeit usually negligible, latency between the object upload and the Lambda invocation.

The next concept to explore is handling S3 object deletion events to trigger Lambda functions.

Want structured learning?

Take the full S3 course →