How to trigger .NET Lambdas with S3 Events

When a file lands in S3, sometimes you want that to automatically trigger backend processing: image resizing, virus scanning, or sending a notification. AWS makes this possible using S3 event notifications to trigger Lambda functions. In this post, we’ll walk through wiring an S3 upload event to a .NET Lambda function.

Prerequisites

  • .NET 6 or later SDK

  • AWS CLI (configured with credentials)

  • Amazon.Lambda.Tools CLI (dotnet tool install -g Amazon.Lambda.Tools)

  • An existing or new S3 bucket

Step 1: Create the Lambda Project

Start with the empty Lambda template:

dotnet new lambda.EmptyFunction --name S3TriggeredLambda
cd S3TriggeredLambda

Step 2: Define the Handler for S3 Events

Update Function.cs to accept an S3Event and log the key of the uploaded file:

using Amazon.Lambda.S3Events;
using Amazon.S3;
using Amazon.S3.Util;
using Amazon.Lambda.Core;

[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))]

public class Function
{
    private readonly IAmazonS3 _s3Client;

    public Function() : this(new AmazonS3Client()) { }

    public Function(IAmazonS3 s3Client)
    {
        _s3Client = s3Client;
    }

    public async Task FunctionHandler(S3Event evnt, ILambdaContext context)
    {
        foreach (var record in evnt.Records)
        {
            var bucket = record.S3.Bucket.Name;
            var key = record.S3.Object.Key;

            context.Logger.LogLine($"File uploaded to S3: {bucket}/{key}");

            // Optional: read content
            var response = await _s3Client.GetObjectAsync(bucket, key);
            using var reader = new StreamReader(response.ResponseStream);
            var content = await reader.ReadToEndAsync();

            context.Logger.LogLine($"First 100 characters: {content[..Math.Min(100, content.Length)]}");
        }
    }
}

Step 3: Configure Lambda Deployment

Create aws-lambda-tools-defaults.json:

{
  "profile": "default",
  "region": "us-east-1",
  "configuration": "Release",
  "framework": "net6.0",
  "function-runtime": "dotnet6",
  "function-handler": "S3TriggeredLambda::S3TriggeredLambda.Function::FunctionHandler",
  "function-memory-size": 256,
  "function-timeout": 30,
  "function-name": "S3TriggeredLambda",
  "function-role": "arn:aws:iam::123456789012:role/your-lambda-role"
}

Step 4: Deploy the Function

dotnet lambda deploy-function

Take note of the function name or ARN printed after deployment.

Step 5: Configure S3 to Trigger the Lambda

Use the AWS CLI or Console to attach the Lambda as a notification target for your S3 bucket.

Option A: Using AWS Console

  • Go to your S3 bucket

  • Choose Properties > Event notifications

  • Create a new event

    • Event type: PUT

    • Destination: Lambda function

    • Select your Lambda

Option B: Using AWS CLI

aws s3api put-bucket-notification-configuration --bucket your-bucket-name --notification-configuration '{
  "LambdaFunctionConfigurations": [
    {
      "LambdaFunctionArn": "arn:aws:lambda:us-east-1:123456789012:function:S3TriggeredLambda",
      "Events": ["s3:ObjectCreated:*"]
    }
  ]
}'

Make sure your Lambda function has permission to be invoked by S3:

aws lambda add-permission \
  --function-name S3TriggeredLambda \
  --principal s3.amazonaws.com \
  --statement-id s3invoke \
  --action "lambda:InvokeFunction" \
  --source-arn arn:aws:s3:::your-bucket-name

Step 6: Test the Integration

Upload a file to your S3 bucket:

aws s3 cp sample.txt s3://your-bucket-name/

Then check the Lambda logs:

aws logs describe-log-groups
aws logs get-log-events --log-group-name /aws/lambda/S3TriggeredLambda --log-stream-name <latest-stream-name>

You should see log lines indicating that your Lambda processed the S3 object.

Cleanup

To avoid charges, delete the Lambda function and remove the S3 notification:

aws lambda delete-function --function-name S3TriggeredLambda

Summary

You’ve now connected S3 with a .NET Lambda to react to file uploads in real time. This pattern is ideal for file processing pipelines, ingestion workflows, or triggering downstream services without managing infrastructure.

References

0
Subscribe to my newsletter

Read articles from Renato Ramos Nascimento directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Renato Ramos Nascimento
Renato Ramos Nascimento

With over 14 years in software development, I specialize in backend systems using .NET, Python, and Java. I bring full lifecycle expertise, including requirements analysis, client/server and data layer development, automated testing (unit, integration, end-to-end), and CI/CD implementations using Docker, GitLab Pipelines, GitHub Actions, Terraform, and AWS CodeStar.