The aws:s3/bucketNotification:BucketNotification resource, part of the Pulumi AWS provider, configures S3 event notifications that trigger when objects are created, removed, or restored in a bucket. This guide focuses on four capabilities: Lambda function invocation, SNS topic publishing, SQS queue delivery, and EventBridge routing.
Each S3 bucket supports only one notification configuration resource. Notifications require destination resources with policies granting S3 permission to invoke or publish. The examples are intentionally small. Combine them with your own Lambda functions, topics, queues, and IAM policies.
Invoke Lambda functions when objects are created
Many serverless workflows begin when files land in S3. Lambda functions process uploads immediately, transforming data or triggering downstream pipelines.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const assumeRole = aws.iam.getPolicyDocument({
statements: [{
effect: "Allow",
principals: [{
type: "Service",
identifiers: ["lambda.amazonaws.com"],
}],
actions: ["sts:AssumeRole"],
}],
});
const iamForLambda = new aws.iam.Role("iam_for_lambda", {
name: "iam_for_lambda",
assumeRolePolicy: assumeRole.then(assumeRole => assumeRole.json),
});
const func = new aws.lambda.Function("func", {
code: new pulumi.asset.FileArchive("your-function.zip"),
name: "example_lambda_name",
role: iamForLambda.arn,
handler: "exports.example",
runtime: aws.lambda.Runtime.NodeJS20dX,
});
const bucket = new aws.s3.Bucket("bucket", {bucket: "your-bucket-name"});
const allowBucket = new aws.lambda.Permission("allow_bucket", {
statementId: "AllowExecutionFromS3Bucket",
action: "lambda:InvokeFunction",
"function": func.arn,
principal: "s3.amazonaws.com",
sourceArn: bucket.arn,
});
const bucketNotification = new aws.s3.BucketNotification("bucket_notification", {
bucket: bucket.id,
lambdaFunctions: [{
lambdaFunctionArn: func.arn,
events: ["s3:ObjectCreated:*"],
filterPrefix: "AWSLogs/",
filterSuffix: ".log",
}],
}, {
dependsOn: [allowBucket],
});
import pulumi
import pulumi_aws as aws
assume_role = aws.iam.get_policy_document(statements=[{
"effect": "Allow",
"principals": [{
"type": "Service",
"identifiers": ["lambda.amazonaws.com"],
}],
"actions": ["sts:AssumeRole"],
}])
iam_for_lambda = aws.iam.Role("iam_for_lambda",
name="iam_for_lambda",
assume_role_policy=assume_role.json)
func = aws.lambda_.Function("func",
code=pulumi.FileArchive("your-function.zip"),
name="example_lambda_name",
role=iam_for_lambda.arn,
handler="exports.example",
runtime=aws.lambda_.Runtime.NODE_JS20D_X)
bucket = aws.s3.Bucket("bucket", bucket="your-bucket-name")
allow_bucket = aws.lambda_.Permission("allow_bucket",
statement_id="AllowExecutionFromS3Bucket",
action="lambda:InvokeFunction",
function=func.arn,
principal="s3.amazonaws.com",
source_arn=bucket.arn)
bucket_notification = aws.s3.BucketNotification("bucket_notification",
bucket=bucket.id,
lambda_functions=[{
"lambda_function_arn": func.arn,
"events": ["s3:ObjectCreated:*"],
"filter_prefix": "AWSLogs/",
"filter_suffix": ".log",
}],
opts = pulumi.ResourceOptions(depends_on=[allow_bucket]))
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/iam"
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/lambda"
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/s3"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
assumeRole, err := iam.GetPolicyDocument(ctx, &iam.GetPolicyDocumentArgs{
Statements: []iam.GetPolicyDocumentStatement{
{
Effect: pulumi.StringRef("Allow"),
Principals: []iam.GetPolicyDocumentStatementPrincipal{
{
Type: "Service",
Identifiers: []string{
"lambda.amazonaws.com",
},
},
},
Actions: []string{
"sts:AssumeRole",
},
},
},
}, nil)
if err != nil {
return err
}
iamForLambda, err := iam.NewRole(ctx, "iam_for_lambda", &iam.RoleArgs{
Name: pulumi.String("iam_for_lambda"),
AssumeRolePolicy: pulumi.String(assumeRole.Json),
})
if err != nil {
return err
}
_func, err := lambda.NewFunction(ctx, "func", &lambda.FunctionArgs{
Code: pulumi.NewFileArchive("your-function.zip"),
Name: pulumi.String("example_lambda_name"),
Role: iamForLambda.Arn,
Handler: pulumi.String("exports.example"),
Runtime: pulumi.String(lambda.RuntimeNodeJS20dX),
})
if err != nil {
return err
}
bucket, err := s3.NewBucket(ctx, "bucket", &s3.BucketArgs{
Bucket: pulumi.String("your-bucket-name"),
})
if err != nil {
return err
}
allowBucket, err := lambda.NewPermission(ctx, "allow_bucket", &lambda.PermissionArgs{
StatementId: pulumi.String("AllowExecutionFromS3Bucket"),
Action: pulumi.String("lambda:InvokeFunction"),
Function: _func.Arn,
Principal: pulumi.String("s3.amazonaws.com"),
SourceArn: bucket.Arn,
})
if err != nil {
return err
}
_, err = s3.NewBucketNotification(ctx, "bucket_notification", &s3.BucketNotificationArgs{
Bucket: bucket.ID(),
LambdaFunctions: s3.BucketNotificationLambdaFunctionArray{
&s3.BucketNotificationLambdaFunctionArgs{
LambdaFunctionArn: _func.Arn,
Events: pulumi.StringArray{
pulumi.String("s3:ObjectCreated:*"),
},
FilterPrefix: pulumi.String("AWSLogs/"),
FilterSuffix: pulumi.String(".log"),
},
},
}, pulumi.DependsOn([]pulumi.Resource{
allowBucket,
}))
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;
return await Deployment.RunAsync(() =>
{
var assumeRole = Aws.Iam.GetPolicyDocument.Invoke(new()
{
Statements = new[]
{
new Aws.Iam.Inputs.GetPolicyDocumentStatementInputArgs
{
Effect = "Allow",
Principals = new[]
{
new Aws.Iam.Inputs.GetPolicyDocumentStatementPrincipalInputArgs
{
Type = "Service",
Identifiers = new[]
{
"lambda.amazonaws.com",
},
},
},
Actions = new[]
{
"sts:AssumeRole",
},
},
},
});
var iamForLambda = new Aws.Iam.Role("iam_for_lambda", new()
{
Name = "iam_for_lambda",
AssumeRolePolicy = assumeRole.Apply(getPolicyDocumentResult => getPolicyDocumentResult.Json),
});
var func = new Aws.Lambda.Function("func", new()
{
Code = new FileArchive("your-function.zip"),
Name = "example_lambda_name",
Role = iamForLambda.Arn,
Handler = "exports.example",
Runtime = Aws.Lambda.Runtime.NodeJS20dX,
});
var bucket = new Aws.S3.Bucket("bucket", new()
{
BucketName = "your-bucket-name",
});
var allowBucket = new Aws.Lambda.Permission("allow_bucket", new()
{
StatementId = "AllowExecutionFromS3Bucket",
Action = "lambda:InvokeFunction",
Function = func.Arn,
Principal = "s3.amazonaws.com",
SourceArn = bucket.Arn,
});
var bucketNotification = new Aws.S3.BucketNotification("bucket_notification", new()
{
Bucket = bucket.Id,
LambdaFunctions = new[]
{
new Aws.S3.Inputs.BucketNotificationLambdaFunctionArgs
{
LambdaFunctionArn = func.Arn,
Events = new[]
{
"s3:ObjectCreated:*",
},
FilterPrefix = "AWSLogs/",
FilterSuffix = ".log",
},
},
}, new CustomResourceOptions
{
DependsOn =
{
allowBucket,
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.iam.IamFunctions;
import com.pulumi.aws.iam.inputs.GetPolicyDocumentArgs;
import com.pulumi.aws.iam.Role;
import com.pulumi.aws.iam.RoleArgs;
import com.pulumi.aws.lambda.Function;
import com.pulumi.aws.lambda.FunctionArgs;
import com.pulumi.aws.s3.Bucket;
import com.pulumi.aws.s3.BucketArgs;
import com.pulumi.aws.lambda.Permission;
import com.pulumi.aws.lambda.PermissionArgs;
import com.pulumi.aws.s3.BucketNotification;
import com.pulumi.aws.s3.BucketNotificationArgs;
import com.pulumi.aws.s3.inputs.BucketNotificationLambdaFunctionArgs;
import com.pulumi.asset.FileArchive;
import com.pulumi.resources.CustomResourceOptions;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
public class App {
public static void main(String[] args) {
Pulumi.run(App::stack);
}
public static void stack(Context ctx) {
final var assumeRole = IamFunctions.getPolicyDocument(GetPolicyDocumentArgs.builder()
.statements(GetPolicyDocumentStatementArgs.builder()
.effect("Allow")
.principals(GetPolicyDocumentStatementPrincipalArgs.builder()
.type("Service")
.identifiers("lambda.amazonaws.com")
.build())
.actions("sts:AssumeRole")
.build())
.build());
var iamForLambda = new Role("iamForLambda", RoleArgs.builder()
.name("iam_for_lambda")
.assumeRolePolicy(assumeRole.json())
.build());
var func = new Function("func", FunctionArgs.builder()
.code(new FileArchive("your-function.zip"))
.name("example_lambda_name")
.role(iamForLambda.arn())
.handler("exports.example")
.runtime("nodejs20.x")
.build());
var bucket = new Bucket("bucket", BucketArgs.builder()
.bucket("your-bucket-name")
.build());
var allowBucket = new Permission("allowBucket", PermissionArgs.builder()
.statementId("AllowExecutionFromS3Bucket")
.action("lambda:InvokeFunction")
.function(func.arn())
.principal("s3.amazonaws.com")
.sourceArn(bucket.arn())
.build());
var bucketNotification = new BucketNotification("bucketNotification", BucketNotificationArgs.builder()
.bucket(bucket.id())
.lambdaFunctions(BucketNotificationLambdaFunctionArgs.builder()
.lambdaFunctionArn(func.arn())
.events("s3:ObjectCreated:*")
.filterPrefix("AWSLogs/")
.filterSuffix(".log")
.build())
.build(), CustomResourceOptions.builder()
.dependsOn(allowBucket)
.build());
}
}
resources:
iamForLambda:
type: aws:iam:Role
name: iam_for_lambda
properties:
name: iam_for_lambda
assumeRolePolicy: ${assumeRole.json}
allowBucket:
type: aws:lambda:Permission
name: allow_bucket
properties:
statementId: AllowExecutionFromS3Bucket
action: lambda:InvokeFunction
function: ${func.arn}
principal: s3.amazonaws.com
sourceArn: ${bucket.arn}
func:
type: aws:lambda:Function
properties:
code:
fn::FileArchive: your-function.zip
name: example_lambda_name
role: ${iamForLambda.arn}
handler: exports.example
runtime: nodejs20.x
bucket:
type: aws:s3:Bucket
properties:
bucket: your-bucket-name
bucketNotification:
type: aws:s3:BucketNotification
name: bucket_notification
properties:
bucket: ${bucket.id}
lambdaFunctions:
- lambdaFunctionArn: ${func.arn}
events:
- s3:ObjectCreated:*
filterPrefix: AWSLogs/
filterSuffix: .log
options:
dependsOn:
- ${allowBucket}
variables:
assumeRole:
fn::invoke:
function: aws:iam:getPolicyDocument
arguments:
statements:
- effect: Allow
principals:
- type: Service
identifiers:
- lambda.amazonaws.com
actions:
- sts:AssumeRole
When objects matching the filter land in the bucket, S3 invokes the Lambda function. The lambdaFunctions array specifies the function ARN, event types to monitor, and optional filterPrefix and filterSuffix for path-based routing. The lambda.Permission resource grants S3 permission to invoke the function; without it, invocations fail with access denied errors.
Route different prefixes to separate Lambda functions
Applications often need different processing logic for different file types or paths. A single notification configuration routes objects to multiple Lambda functions based on prefix patterns.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const assumeRole = aws.iam.getPolicyDocument({
statements: [{
effect: "Allow",
principals: [{
type: "Service",
identifiers: ["lambda.amazonaws.com"],
}],
actions: ["sts:AssumeRole"],
}],
});
const iamForLambda = new aws.iam.Role("iam_for_lambda", {
name: "iam_for_lambda",
assumeRolePolicy: assumeRole.then(assumeRole => assumeRole.json),
});
const func1 = new aws.lambda.Function("func1", {
code: new pulumi.asset.FileArchive("your-function1.zip"),
name: "example_lambda_name1",
role: iamForLambda.arn,
handler: "exports.example",
runtime: aws.lambda.Runtime.NodeJS20dX,
});
const bucket = new aws.s3.Bucket("bucket", {bucket: "your-bucket-name"});
const allowBucket1 = new aws.lambda.Permission("allow_bucket1", {
statementId: "AllowExecutionFromS3Bucket1",
action: "lambda:InvokeFunction",
"function": func1.arn,
principal: "s3.amazonaws.com",
sourceArn: bucket.arn,
});
const func2 = new aws.lambda.Function("func2", {
code: new pulumi.asset.FileArchive("your-function2.zip"),
name: "example_lambda_name2",
role: iamForLambda.arn,
handler: "exports.example",
});
const allowBucket2 = new aws.lambda.Permission("allow_bucket2", {
statementId: "AllowExecutionFromS3Bucket2",
action: "lambda:InvokeFunction",
"function": func2.arn,
principal: "s3.amazonaws.com",
sourceArn: bucket.arn,
});
const bucketNotification = new aws.s3.BucketNotification("bucket_notification", {
bucket: bucket.id,
lambdaFunctions: [
{
lambdaFunctionArn: func1.arn,
events: ["s3:ObjectCreated:*"],
filterPrefix: "AWSLogs/",
filterSuffix: ".log",
},
{
lambdaFunctionArn: func2.arn,
events: ["s3:ObjectCreated:*"],
filterPrefix: "OtherLogs/",
filterSuffix: ".log",
},
],
}, {
dependsOn: [
allowBucket1,
allowBucket2,
],
});
import pulumi
import pulumi_aws as aws
assume_role = aws.iam.get_policy_document(statements=[{
"effect": "Allow",
"principals": [{
"type": "Service",
"identifiers": ["lambda.amazonaws.com"],
}],
"actions": ["sts:AssumeRole"],
}])
iam_for_lambda = aws.iam.Role("iam_for_lambda",
name="iam_for_lambda",
assume_role_policy=assume_role.json)
func1 = aws.lambda_.Function("func1",
code=pulumi.FileArchive("your-function1.zip"),
name="example_lambda_name1",
role=iam_for_lambda.arn,
handler="exports.example",
runtime=aws.lambda_.Runtime.NODE_JS20D_X)
bucket = aws.s3.Bucket("bucket", bucket="your-bucket-name")
allow_bucket1 = aws.lambda_.Permission("allow_bucket1",
statement_id="AllowExecutionFromS3Bucket1",
action="lambda:InvokeFunction",
function=func1.arn,
principal="s3.amazonaws.com",
source_arn=bucket.arn)
func2 = aws.lambda_.Function("func2",
code=pulumi.FileArchive("your-function2.zip"),
name="example_lambda_name2",
role=iam_for_lambda.arn,
handler="exports.example")
allow_bucket2 = aws.lambda_.Permission("allow_bucket2",
statement_id="AllowExecutionFromS3Bucket2",
action="lambda:InvokeFunction",
function=func2.arn,
principal="s3.amazonaws.com",
source_arn=bucket.arn)
bucket_notification = aws.s3.BucketNotification("bucket_notification",
bucket=bucket.id,
lambda_functions=[
{
"lambda_function_arn": func1.arn,
"events": ["s3:ObjectCreated:*"],
"filter_prefix": "AWSLogs/",
"filter_suffix": ".log",
},
{
"lambda_function_arn": func2.arn,
"events": ["s3:ObjectCreated:*"],
"filter_prefix": "OtherLogs/",
"filter_suffix": ".log",
},
],
opts = pulumi.ResourceOptions(depends_on=[
allow_bucket1,
allow_bucket2,
]))
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/iam"
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/lambda"
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/s3"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
assumeRole, err := iam.GetPolicyDocument(ctx, &iam.GetPolicyDocumentArgs{
Statements: []iam.GetPolicyDocumentStatement{
{
Effect: pulumi.StringRef("Allow"),
Principals: []iam.GetPolicyDocumentStatementPrincipal{
{
Type: "Service",
Identifiers: []string{
"lambda.amazonaws.com",
},
},
},
Actions: []string{
"sts:AssumeRole",
},
},
},
}, nil)
if err != nil {
return err
}
iamForLambda, err := iam.NewRole(ctx, "iam_for_lambda", &iam.RoleArgs{
Name: pulumi.String("iam_for_lambda"),
AssumeRolePolicy: pulumi.String(assumeRole.Json),
})
if err != nil {
return err
}
func1, err := lambda.NewFunction(ctx, "func1", &lambda.FunctionArgs{
Code: pulumi.NewFileArchive("your-function1.zip"),
Name: pulumi.String("example_lambda_name1"),
Role: iamForLambda.Arn,
Handler: pulumi.String("exports.example"),
Runtime: pulumi.String(lambda.RuntimeNodeJS20dX),
})
if err != nil {
return err
}
bucket, err := s3.NewBucket(ctx, "bucket", &s3.BucketArgs{
Bucket: pulumi.String("your-bucket-name"),
})
if err != nil {
return err
}
allowBucket1, err := lambda.NewPermission(ctx, "allow_bucket1", &lambda.PermissionArgs{
StatementId: pulumi.String("AllowExecutionFromS3Bucket1"),
Action: pulumi.String("lambda:InvokeFunction"),
Function: func1.Arn,
Principal: pulumi.String("s3.amazonaws.com"),
SourceArn: bucket.Arn,
})
if err != nil {
return err
}
func2, err := lambda.NewFunction(ctx, "func2", &lambda.FunctionArgs{
Code: pulumi.NewFileArchive("your-function2.zip"),
Name: pulumi.String("example_lambda_name2"),
Role: iamForLambda.Arn,
Handler: pulumi.String("exports.example"),
})
if err != nil {
return err
}
allowBucket2, err := lambda.NewPermission(ctx, "allow_bucket2", &lambda.PermissionArgs{
StatementId: pulumi.String("AllowExecutionFromS3Bucket2"),
Action: pulumi.String("lambda:InvokeFunction"),
Function: func2.Arn,
Principal: pulumi.String("s3.amazonaws.com"),
SourceArn: bucket.Arn,
})
if err != nil {
return err
}
_, err = s3.NewBucketNotification(ctx, "bucket_notification", &s3.BucketNotificationArgs{
Bucket: bucket.ID(),
LambdaFunctions: s3.BucketNotificationLambdaFunctionArray{
&s3.BucketNotificationLambdaFunctionArgs{
LambdaFunctionArn: func1.Arn,
Events: pulumi.StringArray{
pulumi.String("s3:ObjectCreated:*"),
},
FilterPrefix: pulumi.String("AWSLogs/"),
FilterSuffix: pulumi.String(".log"),
},
&s3.BucketNotificationLambdaFunctionArgs{
LambdaFunctionArn: func2.Arn,
Events: pulumi.StringArray{
pulumi.String("s3:ObjectCreated:*"),
},
FilterPrefix: pulumi.String("OtherLogs/"),
FilterSuffix: pulumi.String(".log"),
},
},
}, pulumi.DependsOn([]pulumi.Resource{
allowBucket1,
allowBucket2,
}))
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;
return await Deployment.RunAsync(() =>
{
var assumeRole = Aws.Iam.GetPolicyDocument.Invoke(new()
{
Statements = new[]
{
new Aws.Iam.Inputs.GetPolicyDocumentStatementInputArgs
{
Effect = "Allow",
Principals = new[]
{
new Aws.Iam.Inputs.GetPolicyDocumentStatementPrincipalInputArgs
{
Type = "Service",
Identifiers = new[]
{
"lambda.amazonaws.com",
},
},
},
Actions = new[]
{
"sts:AssumeRole",
},
},
},
});
var iamForLambda = new Aws.Iam.Role("iam_for_lambda", new()
{
Name = "iam_for_lambda",
AssumeRolePolicy = assumeRole.Apply(getPolicyDocumentResult => getPolicyDocumentResult.Json),
});
var func1 = new Aws.Lambda.Function("func1", new()
{
Code = new FileArchive("your-function1.zip"),
Name = "example_lambda_name1",
Role = iamForLambda.Arn,
Handler = "exports.example",
Runtime = Aws.Lambda.Runtime.NodeJS20dX,
});
var bucket = new Aws.S3.Bucket("bucket", new()
{
BucketName = "your-bucket-name",
});
var allowBucket1 = new Aws.Lambda.Permission("allow_bucket1", new()
{
StatementId = "AllowExecutionFromS3Bucket1",
Action = "lambda:InvokeFunction",
Function = func1.Arn,
Principal = "s3.amazonaws.com",
SourceArn = bucket.Arn,
});
var func2 = new Aws.Lambda.Function("func2", new()
{
Code = new FileArchive("your-function2.zip"),
Name = "example_lambda_name2",
Role = iamForLambda.Arn,
Handler = "exports.example",
});
var allowBucket2 = new Aws.Lambda.Permission("allow_bucket2", new()
{
StatementId = "AllowExecutionFromS3Bucket2",
Action = "lambda:InvokeFunction",
Function = func2.Arn,
Principal = "s3.amazonaws.com",
SourceArn = bucket.Arn,
});
var bucketNotification = new Aws.S3.BucketNotification("bucket_notification", new()
{
Bucket = bucket.Id,
LambdaFunctions = new[]
{
new Aws.S3.Inputs.BucketNotificationLambdaFunctionArgs
{
LambdaFunctionArn = func1.Arn,
Events = new[]
{
"s3:ObjectCreated:*",
},
FilterPrefix = "AWSLogs/",
FilterSuffix = ".log",
},
new Aws.S3.Inputs.BucketNotificationLambdaFunctionArgs
{
LambdaFunctionArn = func2.Arn,
Events = new[]
{
"s3:ObjectCreated:*",
},
FilterPrefix = "OtherLogs/",
FilterSuffix = ".log",
},
},
}, new CustomResourceOptions
{
DependsOn =
{
allowBucket1,
allowBucket2,
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.iam.IamFunctions;
import com.pulumi.aws.iam.inputs.GetPolicyDocumentArgs;
import com.pulumi.aws.iam.Role;
import com.pulumi.aws.iam.RoleArgs;
import com.pulumi.aws.lambda.Function;
import com.pulumi.aws.lambda.FunctionArgs;
import com.pulumi.aws.s3.Bucket;
import com.pulumi.aws.s3.BucketArgs;
import com.pulumi.aws.lambda.Permission;
import com.pulumi.aws.lambda.PermissionArgs;
import com.pulumi.aws.s3.BucketNotification;
import com.pulumi.aws.s3.BucketNotificationArgs;
import com.pulumi.aws.s3.inputs.BucketNotificationLambdaFunctionArgs;
import com.pulumi.asset.FileArchive;
import com.pulumi.resources.CustomResourceOptions;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
public class App {
public static void main(String[] args) {
Pulumi.run(App::stack);
}
public static void stack(Context ctx) {
final var assumeRole = IamFunctions.getPolicyDocument(GetPolicyDocumentArgs.builder()
.statements(GetPolicyDocumentStatementArgs.builder()
.effect("Allow")
.principals(GetPolicyDocumentStatementPrincipalArgs.builder()
.type("Service")
.identifiers("lambda.amazonaws.com")
.build())
.actions("sts:AssumeRole")
.build())
.build());
var iamForLambda = new Role("iamForLambda", RoleArgs.builder()
.name("iam_for_lambda")
.assumeRolePolicy(assumeRole.json())
.build());
var func1 = new Function("func1", FunctionArgs.builder()
.code(new FileArchive("your-function1.zip"))
.name("example_lambda_name1")
.role(iamForLambda.arn())
.handler("exports.example")
.runtime("nodejs20.x")
.build());
var bucket = new Bucket("bucket", BucketArgs.builder()
.bucket("your-bucket-name")
.build());
var allowBucket1 = new Permission("allowBucket1", PermissionArgs.builder()
.statementId("AllowExecutionFromS3Bucket1")
.action("lambda:InvokeFunction")
.function(func1.arn())
.principal("s3.amazonaws.com")
.sourceArn(bucket.arn())
.build());
var func2 = new Function("func2", FunctionArgs.builder()
.code(new FileArchive("your-function2.zip"))
.name("example_lambda_name2")
.role(iamForLambda.arn())
.handler("exports.example")
.build());
var allowBucket2 = new Permission("allowBucket2", PermissionArgs.builder()
.statementId("AllowExecutionFromS3Bucket2")
.action("lambda:InvokeFunction")
.function(func2.arn())
.principal("s3.amazonaws.com")
.sourceArn(bucket.arn())
.build());
var bucketNotification = new BucketNotification("bucketNotification", BucketNotificationArgs.builder()
.bucket(bucket.id())
.lambdaFunctions(
BucketNotificationLambdaFunctionArgs.builder()
.lambdaFunctionArn(func1.arn())
.events("s3:ObjectCreated:*")
.filterPrefix("AWSLogs/")
.filterSuffix(".log")
.build(),
BucketNotificationLambdaFunctionArgs.builder()
.lambdaFunctionArn(func2.arn())
.events("s3:ObjectCreated:*")
.filterPrefix("OtherLogs/")
.filterSuffix(".log")
.build())
.build(), CustomResourceOptions.builder()
.dependsOn(
allowBucket1,
allowBucket2)
.build());
}
}
resources:
iamForLambda:
type: aws:iam:Role
name: iam_for_lambda
properties:
name: iam_for_lambda
assumeRolePolicy: ${assumeRole.json}
allowBucket1:
type: aws:lambda:Permission
name: allow_bucket1
properties:
statementId: AllowExecutionFromS3Bucket1
action: lambda:InvokeFunction
function: ${func1.arn}
principal: s3.amazonaws.com
sourceArn: ${bucket.arn}
func1:
type: aws:lambda:Function
properties:
code:
fn::FileArchive: your-function1.zip
name: example_lambda_name1
role: ${iamForLambda.arn}
handler: exports.example
runtime: nodejs20.x
allowBucket2:
type: aws:lambda:Permission
name: allow_bucket2
properties:
statementId: AllowExecutionFromS3Bucket2
action: lambda:InvokeFunction
function: ${func2.arn}
principal: s3.amazonaws.com
sourceArn: ${bucket.arn}
func2:
type: aws:lambda:Function
properties:
code:
fn::FileArchive: your-function2.zip
name: example_lambda_name2
role: ${iamForLambda.arn}
handler: exports.example
bucket:
type: aws:s3:Bucket
properties:
bucket: your-bucket-name
bucketNotification:
type: aws:s3:BucketNotification
name: bucket_notification
properties:
bucket: ${bucket.id}
lambdaFunctions:
- lambdaFunctionArn: ${func1.arn}
events:
- s3:ObjectCreated:*
filterPrefix: AWSLogs/
filterSuffix: .log
- lambdaFunctionArn: ${func2.arn}
events:
- s3:ObjectCreated:*
filterPrefix: OtherLogs/
filterSuffix: .log
options:
dependsOn:
- ${allowBucket1}
- ${allowBucket2}
variables:
assumeRole:
fn::invoke:
function: aws:iam:getPolicyDocument
arguments:
statements:
- effect: Allow
principals:
- type: Service
identifiers:
- lambda.amazonaws.com
actions:
- sts:AssumeRole
Each entry in the lambdaFunctions array defines a separate routing rule. Here, objects under “AWSLogs/” trigger func1, while “OtherLogs/” objects trigger func2. The dependsOn ensures both Permission resources exist before creating the notification configuration, preventing race conditions during deployment.
Publish events to SNS topics for fan-out
SNS topics enable fan-out patterns where multiple subscribers receive the same S3 event, supporting workflows where uploads trigger notifications to multiple systems or teams.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const bucket = new aws.s3.Bucket("bucket", {bucket: "your-bucket-name"});
const topic = aws.iam.getPolicyDocumentOutput({
statements: [{
effect: "Allow",
principals: [{
type: "Service",
identifiers: ["s3.amazonaws.com"],
}],
actions: ["SNS:Publish"],
resources: ["arn:aws:sns:*:*:s3-event-notification-topic"],
conditions: [{
test: "ArnLike",
variable: "aws:SourceArn",
values: [bucket.arn],
}],
}],
});
const topicTopic = new aws.sns.Topic("topic", {
name: "s3-event-notification-topic",
policy: topic.apply(topic => topic.json),
});
const bucketNotification = new aws.s3.BucketNotification("bucket_notification", {
bucket: bucket.id,
topics: [{
topicArn: topicTopic.arn,
events: ["s3:ObjectCreated:*"],
filterSuffix: ".log",
}],
});
import pulumi
import pulumi_aws as aws
bucket = aws.s3.Bucket("bucket", bucket="your-bucket-name")
topic = aws.iam.get_policy_document_output(statements=[{
"effect": "Allow",
"principals": [{
"type": "Service",
"identifiers": ["s3.amazonaws.com"],
}],
"actions": ["SNS:Publish"],
"resources": ["arn:aws:sns:*:*:s3-event-notification-topic"],
"conditions": [{
"test": "ArnLike",
"variable": "aws:SourceArn",
"values": [bucket.arn],
}],
}])
topic_topic = aws.sns.Topic("topic",
name="s3-event-notification-topic",
policy=topic.json)
bucket_notification = aws.s3.BucketNotification("bucket_notification",
bucket=bucket.id,
topics=[{
"topic_arn": topic_topic.arn,
"events": ["s3:ObjectCreated:*"],
"filter_suffix": ".log",
}])
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/iam"
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/s3"
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/sns"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
bucket, err := s3.NewBucket(ctx, "bucket", &s3.BucketArgs{
Bucket: pulumi.String("your-bucket-name"),
})
if err != nil {
return err
}
topic := iam.GetPolicyDocumentOutput(ctx, iam.GetPolicyDocumentOutputArgs{
Statements: iam.GetPolicyDocumentStatementArray{
&iam.GetPolicyDocumentStatementArgs{
Effect: pulumi.String("Allow"),
Principals: iam.GetPolicyDocumentStatementPrincipalArray{
&iam.GetPolicyDocumentStatementPrincipalArgs{
Type: pulumi.String("Service"),
Identifiers: pulumi.StringArray{
pulumi.String("s3.amazonaws.com"),
},
},
},
Actions: pulumi.StringArray{
pulumi.String("SNS:Publish"),
},
Resources: pulumi.StringArray{
pulumi.String("arn:aws:sns:*:*:s3-event-notification-topic"),
},
Conditions: iam.GetPolicyDocumentStatementConditionArray{
&iam.GetPolicyDocumentStatementConditionArgs{
Test: pulumi.String("ArnLike"),
Variable: pulumi.String("aws:SourceArn"),
Values: pulumi.StringArray{
bucket.Arn,
},
},
},
},
},
}, nil)
topicTopic, err := sns.NewTopic(ctx, "topic", &sns.TopicArgs{
Name: pulumi.String("s3-event-notification-topic"),
Policy: pulumi.String(topic.ApplyT(func(topic iam.GetPolicyDocumentResult) (*string, error) {
return &topic.Json, nil
}).(pulumi.StringPtrOutput)),
})
if err != nil {
return err
}
_, err = s3.NewBucketNotification(ctx, "bucket_notification", &s3.BucketNotificationArgs{
Bucket: bucket.ID(),
Topics: s3.BucketNotificationTopicArray{
&s3.BucketNotificationTopicArgs{
TopicArn: topicTopic.Arn,
Events: pulumi.StringArray{
pulumi.String("s3:ObjectCreated:*"),
},
FilterSuffix: pulumi.String(".log"),
},
},
})
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;
return await Deployment.RunAsync(() =>
{
var bucket = new Aws.S3.Bucket("bucket", new()
{
BucketName = "your-bucket-name",
});
var topic = Aws.Iam.GetPolicyDocument.Invoke(new()
{
Statements = new[]
{
new Aws.Iam.Inputs.GetPolicyDocumentStatementInputArgs
{
Effect = "Allow",
Principals = new[]
{
new Aws.Iam.Inputs.GetPolicyDocumentStatementPrincipalInputArgs
{
Type = "Service",
Identifiers = new[]
{
"s3.amazonaws.com",
},
},
},
Actions = new[]
{
"SNS:Publish",
},
Resources = new[]
{
"arn:aws:sns:*:*:s3-event-notification-topic",
},
Conditions = new[]
{
new Aws.Iam.Inputs.GetPolicyDocumentStatementConditionInputArgs
{
Test = "ArnLike",
Variable = "aws:SourceArn",
Values = new[]
{
bucket.Arn,
},
},
},
},
},
});
var topicTopic = new Aws.Sns.Topic("topic", new()
{
Name = "s3-event-notification-topic",
Policy = topic.Apply(getPolicyDocumentResult => getPolicyDocumentResult.Json),
});
var bucketNotification = new Aws.S3.BucketNotification("bucket_notification", new()
{
Bucket = bucket.Id,
Topics = new[]
{
new Aws.S3.Inputs.BucketNotificationTopicArgs
{
TopicArn = topicTopic.Arn,
Events = new[]
{
"s3:ObjectCreated:*",
},
FilterSuffix = ".log",
},
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.s3.Bucket;
import com.pulumi.aws.s3.BucketArgs;
import com.pulumi.aws.iam.IamFunctions;
import com.pulumi.aws.iam.inputs.GetPolicyDocumentArgs;
import com.pulumi.aws.sns.Topic;
import com.pulumi.aws.sns.TopicArgs;
import com.pulumi.aws.s3.BucketNotification;
import com.pulumi.aws.s3.BucketNotificationArgs;
import com.pulumi.aws.s3.inputs.BucketNotificationTopicArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
public class App {
public static void main(String[] args) {
Pulumi.run(App::stack);
}
public static void stack(Context ctx) {
var bucket = new Bucket("bucket", BucketArgs.builder()
.bucket("your-bucket-name")
.build());
final var topic = IamFunctions.getPolicyDocument(GetPolicyDocumentArgs.builder()
.statements(GetPolicyDocumentStatementArgs.builder()
.effect("Allow")
.principals(GetPolicyDocumentStatementPrincipalArgs.builder()
.type("Service")
.identifiers("s3.amazonaws.com")
.build())
.actions("SNS:Publish")
.resources("arn:aws:sns:*:*:s3-event-notification-topic")
.conditions(GetPolicyDocumentStatementConditionArgs.builder()
.test("ArnLike")
.variable("aws:SourceArn")
.values(bucket.arn())
.build())
.build())
.build());
var topicTopic = new Topic("topicTopic", TopicArgs.builder()
.name("s3-event-notification-topic")
.policy(topic.applyValue(_topic -> _topic.json()))
.build());
var bucketNotification = new BucketNotification("bucketNotification", BucketNotificationArgs.builder()
.bucket(bucket.id())
.topics(BucketNotificationTopicArgs.builder()
.topicArn(topicTopic.arn())
.events("s3:ObjectCreated:*")
.filterSuffix(".log")
.build())
.build());
}
}
resources:
topicTopic:
type: aws:sns:Topic
name: topic
properties:
name: s3-event-notification-topic
policy: ${topic.json}
bucket:
type: aws:s3:Bucket
properties:
bucket: your-bucket-name
bucketNotification:
type: aws:s3:BucketNotification
name: bucket_notification
properties:
bucket: ${bucket.id}
topics:
- topicArn: ${topicTopic.arn}
events:
- s3:ObjectCreated:*
filterSuffix: .log
variables:
topic:
fn::invoke:
function: aws:iam:getPolicyDocument
arguments:
statements:
- effect: Allow
principals:
- type: Service
identifiers:
- s3.amazonaws.com
actions:
- SNS:Publish
resources:
- arn:aws:sns:*:*:s3-event-notification-topic
conditions:
- test: ArnLike
variable: aws:SourceArn
values:
- ${bucket.arn}
The topics array specifies the SNS topic ARN and event types. S3 publishes a message to the topic whenever a matching event occurs. The SNS topic policy must allow s3.amazonaws.com to publish; the example shows the required policy document with a condition restricting the source to the specific bucket ARN.
Queue events for asynchronous processing
SQS queues decouple S3 events from processing, allowing consumers to poll at their own pace and handle backpressure gracefully.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const bucket = new aws.s3.Bucket("bucket", {bucket: "your-bucket-name"});
const queue = aws.iam.getPolicyDocumentOutput({
statements: [{
effect: "Allow",
principals: [{
type: "*",
identifiers: ["*"],
}],
actions: ["sqs:SendMessage"],
resources: ["arn:aws:sqs:*:*:s3-event-notification-queue"],
conditions: [{
test: "ArnEquals",
variable: "aws:SourceArn",
values: [bucket.arn],
}],
}],
});
const queueQueue = new aws.sqs.Queue("queue", {
name: "s3-event-notification-queue",
policy: queue.apply(queue => queue.json),
});
const bucketNotification = new aws.s3.BucketNotification("bucket_notification", {
bucket: bucket.id,
queues: [{
queueArn: queueQueue.arn,
events: ["s3:ObjectCreated:*"],
filterSuffix: ".log",
}],
});
import pulumi
import pulumi_aws as aws
bucket = aws.s3.Bucket("bucket", bucket="your-bucket-name")
queue = aws.iam.get_policy_document_output(statements=[{
"effect": "Allow",
"principals": [{
"type": "*",
"identifiers": ["*"],
}],
"actions": ["sqs:SendMessage"],
"resources": ["arn:aws:sqs:*:*:s3-event-notification-queue"],
"conditions": [{
"test": "ArnEquals",
"variable": "aws:SourceArn",
"values": [bucket.arn],
}],
}])
queue_queue = aws.sqs.Queue("queue",
name="s3-event-notification-queue",
policy=queue.json)
bucket_notification = aws.s3.BucketNotification("bucket_notification",
bucket=bucket.id,
queues=[{
"queue_arn": queue_queue.arn,
"events": ["s3:ObjectCreated:*"],
"filter_suffix": ".log",
}])
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/iam"
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/s3"
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/sqs"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
bucket, err := s3.NewBucket(ctx, "bucket", &s3.BucketArgs{
Bucket: pulumi.String("your-bucket-name"),
})
if err != nil {
return err
}
queue := iam.GetPolicyDocumentOutput(ctx, iam.GetPolicyDocumentOutputArgs{
Statements: iam.GetPolicyDocumentStatementArray{
&iam.GetPolicyDocumentStatementArgs{
Effect: pulumi.String("Allow"),
Principals: iam.GetPolicyDocumentStatementPrincipalArray{
&iam.GetPolicyDocumentStatementPrincipalArgs{
Type: pulumi.String("*"),
Identifiers: pulumi.StringArray{
pulumi.String("*"),
},
},
},
Actions: pulumi.StringArray{
pulumi.String("sqs:SendMessage"),
},
Resources: pulumi.StringArray{
pulumi.String("arn:aws:sqs:*:*:s3-event-notification-queue"),
},
Conditions: iam.GetPolicyDocumentStatementConditionArray{
&iam.GetPolicyDocumentStatementConditionArgs{
Test: pulumi.String("ArnEquals"),
Variable: pulumi.String("aws:SourceArn"),
Values: pulumi.StringArray{
bucket.Arn,
},
},
},
},
},
}, nil)
queueQueue, err := sqs.NewQueue(ctx, "queue", &sqs.QueueArgs{
Name: pulumi.String("s3-event-notification-queue"),
Policy: pulumi.String(queue.ApplyT(func(queue iam.GetPolicyDocumentResult) (*string, error) {
return &queue.Json, nil
}).(pulumi.StringPtrOutput)),
})
if err != nil {
return err
}
_, err = s3.NewBucketNotification(ctx, "bucket_notification", &s3.BucketNotificationArgs{
Bucket: bucket.ID(),
Queues: s3.BucketNotificationQueueArray{
&s3.BucketNotificationQueueArgs{
QueueArn: queueQueue.Arn,
Events: pulumi.StringArray{
pulumi.String("s3:ObjectCreated:*"),
},
FilterSuffix: pulumi.String(".log"),
},
},
})
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;
return await Deployment.RunAsync(() =>
{
var bucket = new Aws.S3.Bucket("bucket", new()
{
BucketName = "your-bucket-name",
});
var queue = Aws.Iam.GetPolicyDocument.Invoke(new()
{
Statements = new[]
{
new Aws.Iam.Inputs.GetPolicyDocumentStatementInputArgs
{
Effect = "Allow",
Principals = new[]
{
new Aws.Iam.Inputs.GetPolicyDocumentStatementPrincipalInputArgs
{
Type = "*",
Identifiers = new[]
{
"*",
},
},
},
Actions = new[]
{
"sqs:SendMessage",
},
Resources = new[]
{
"arn:aws:sqs:*:*:s3-event-notification-queue",
},
Conditions = new[]
{
new Aws.Iam.Inputs.GetPolicyDocumentStatementConditionInputArgs
{
Test = "ArnEquals",
Variable = "aws:SourceArn",
Values = new[]
{
bucket.Arn,
},
},
},
},
},
});
var queueQueue = new Aws.Sqs.Queue("queue", new()
{
Name = "s3-event-notification-queue",
Policy = queue.Apply(getPolicyDocumentResult => getPolicyDocumentResult.Json),
});
var bucketNotification = new Aws.S3.BucketNotification("bucket_notification", new()
{
Bucket = bucket.Id,
Queues = new[]
{
new Aws.S3.Inputs.BucketNotificationQueueArgs
{
QueueArn = queueQueue.Arn,
Events = new[]
{
"s3:ObjectCreated:*",
},
FilterSuffix = ".log",
},
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.s3.Bucket;
import com.pulumi.aws.s3.BucketArgs;
import com.pulumi.aws.iam.IamFunctions;
import com.pulumi.aws.iam.inputs.GetPolicyDocumentArgs;
import com.pulumi.aws.sqs.Queue;
import com.pulumi.aws.sqs.QueueArgs;
import com.pulumi.aws.s3.BucketNotification;
import com.pulumi.aws.s3.BucketNotificationArgs;
import com.pulumi.aws.s3.inputs.BucketNotificationQueueArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
public class App {
public static void main(String[] args) {
Pulumi.run(App::stack);
}
public static void stack(Context ctx) {
var bucket = new Bucket("bucket", BucketArgs.builder()
.bucket("your-bucket-name")
.build());
final var queue = IamFunctions.getPolicyDocument(GetPolicyDocumentArgs.builder()
.statements(GetPolicyDocumentStatementArgs.builder()
.effect("Allow")
.principals(GetPolicyDocumentStatementPrincipalArgs.builder()
.type("*")
.identifiers("*")
.build())
.actions("sqs:SendMessage")
.resources("arn:aws:sqs:*:*:s3-event-notification-queue")
.conditions(GetPolicyDocumentStatementConditionArgs.builder()
.test("ArnEquals")
.variable("aws:SourceArn")
.values(bucket.arn())
.build())
.build())
.build());
var queueQueue = new Queue("queueQueue", QueueArgs.builder()
.name("s3-event-notification-queue")
.policy(queue.applyValue(_queue -> _queue.json()))
.build());
var bucketNotification = new BucketNotification("bucketNotification", BucketNotificationArgs.builder()
.bucket(bucket.id())
.queues(BucketNotificationQueueArgs.builder()
.queueArn(queueQueue.arn())
.events("s3:ObjectCreated:*")
.filterSuffix(".log")
.build())
.build());
}
}
resources:
queueQueue:
type: aws:sqs:Queue
name: queue
properties:
name: s3-event-notification-queue
policy: ${queue.json}
bucket:
type: aws:s3:Bucket
properties:
bucket: your-bucket-name
bucketNotification:
type: aws:s3:BucketNotification
name: bucket_notification
properties:
bucket: ${bucket.id}
queues:
- queueArn: ${queueQueue.arn}
events:
- s3:ObjectCreated:*
filterSuffix: .log
variables:
queue:
fn::invoke:
function: aws:iam:getPolicyDocument
arguments:
statements:
- effect: Allow
principals:
- type: '*'
identifiers:
- '*'
actions:
- sqs:SendMessage
resources:
- arn:aws:sqs:*:*:s3-event-notification-queue
conditions:
- test: ArnEquals
variable: aws:SourceArn
values:
- ${bucket.arn}
The queues array configures SQS delivery. S3 sends messages to the queue for each matching event. Like SNS, the SQS queue policy must grant s3.amazonaws.com permission to send messages, with a condition limiting the source to the bucket ARN.
Route events through EventBridge for complex workflows
EventBridge provides advanced routing and filtering capabilities beyond basic S3 notifications. Teams use it to build event-driven architectures with multiple targets and transformation rules.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const bucket = new aws.s3.Bucket("bucket", {bucket: "your-bucket-name"});
const bucketNotification = new aws.s3.BucketNotification("bucket_notification", {
bucket: bucket.id,
eventbridge: true,
});
import pulumi
import pulumi_aws as aws
bucket = aws.s3.Bucket("bucket", bucket="your-bucket-name")
bucket_notification = aws.s3.BucketNotification("bucket_notification",
bucket=bucket.id,
eventbridge=True)
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/s3"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
bucket, err := s3.NewBucket(ctx, "bucket", &s3.BucketArgs{
Bucket: pulumi.String("your-bucket-name"),
})
if err != nil {
return err
}
_, err = s3.NewBucketNotification(ctx, "bucket_notification", &s3.BucketNotificationArgs{
Bucket: bucket.ID(),
Eventbridge: pulumi.Bool(true),
})
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;
return await Deployment.RunAsync(() =>
{
var bucket = new Aws.S3.Bucket("bucket", new()
{
BucketName = "your-bucket-name",
});
var bucketNotification = new Aws.S3.BucketNotification("bucket_notification", new()
{
Bucket = bucket.Id,
Eventbridge = true,
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.s3.Bucket;
import com.pulumi.aws.s3.BucketArgs;
import com.pulumi.aws.s3.BucketNotification;
import com.pulumi.aws.s3.BucketNotificationArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
public class App {
public static void main(String[] args) {
Pulumi.run(App::stack);
}
public static void stack(Context ctx) {
var bucket = new Bucket("bucket", BucketArgs.builder()
.bucket("your-bucket-name")
.build());
var bucketNotification = new BucketNotification("bucketNotification", BucketNotificationArgs.builder()
.bucket(bucket.id())
.eventbridge(true)
.build());
}
}
resources:
bucket:
type: aws:s3:Bucket
properties:
bucket: your-bucket-name
bucketNotification:
type: aws:s3:BucketNotification
name: bucket_notification
properties:
bucket: ${bucket.id}
eventbridge: true
Setting eventbridge to true enables EventBridge delivery for all bucket events. Unlike Lambda, SNS, or SQS configurations, EventBridge doesn’t require filter configuration in the notification resource; you define routing rules in EventBridge itself.
Beyond these examples
These snippets focus on specific notification configuration features: Lambda, SNS, and SQS notification targets, prefix and suffix filtering, and EventBridge integration. They’re intentionally minimal rather than full event-driven applications.
The examples reference pre-existing infrastructure such as S3 buckets, Lambda functions with execution roles, and SNS topics and SQS queues with appropriate policies. They focus on configuring the notification rather than provisioning everything around it.
To keep things focused, common notification patterns are omitted, including:
- Event type filtering beyond ObjectCreated (ObjectRemoved, ObjectRestore)
- Multiple event types per notification configuration
- Complex filter combinations (prefix AND suffix patterns)
- Notification IDs for tracking specific configurations
These omissions are intentional: the goal is to illustrate how each notification target is wired, not provide drop-in event processing modules. See the S3 BucketNotification resource reference for all available configuration options.
Let's configure AWS S3 Bucket Event Notifications
Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.
Try Pulumi Cloud for FREEFrequently Asked Questions
Common Issues & Limitations
BucketNotification resources for the same bucket causes perpetual configuration differences. Use a single resource with arrays (lambdaFunctions, queues, topics) to configure multiple triggers.bucket property is immutable and cannot be changed after creation. You must destroy and recreate the resource to change buckets.IAM Permissions & Setup
aws.lambda.Permission resource with action set to lambda:InvokeFunction, principal set to s3.amazonaws.com, and sourceArn set to the bucket ARN. Use dependsOn to ensure the Permission is created before the BucketNotification.s3.amazonaws.com principal to perform the SNS:Publish action, with a condition checking that aws:SourceArn matches the bucket ARN.sqs:SendMessage action, with a condition checking that aws:SourceArn equals the bucket ARN.Multiple Notifications & Filtering
lambdaFunctions property with different filterPrefix values for each function. Ensure all required aws.lambda.Permission resources are created and referenced in dependsOn.queues property, with each entry having a unique id and different filterPrefix values to route events based on object key prefixes.filterPrefix to filter by object key prefix (e.g., images/, AWSLogs/) and filterSuffix to filter by suffix (e.g., .log, .jpg). Both can be combined in the same notification configuration.