How to trigger .NET Lambdas with S3 Events

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.
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
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.




