The aws:sesv2/configurationSetEventDestination:ConfigurationSetEventDestination resource, part of the Pulumi AWS provider, routes SES email events (sends, bounces, complaints, deliveries) to monitoring and analytics destinations. This guide focuses on three capabilities: CloudWatch metrics with custom dimensions, Kinesis Firehose streaming for archival, and SNS topic publishing for real-time alerts.
Event destinations attach to SESv2 configuration sets and reference existing AWS services. The examples are intentionally small. Combine them with your own configuration sets, IAM roles, and destination infrastructure.
Send email events to CloudWatch for metrics and dashboards
Teams monitoring email delivery route SES events to CloudWatch to build custom metrics and dashboards.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const example = new aws.sesv2.ConfigurationSet("example", {configurationSetName: "example"});
const exampleConfigurationSetEventDestination = new aws.sesv2.ConfigurationSetEventDestination("example", {
configurationSetName: example.configurationSetName,
eventDestinationName: "example",
eventDestination: {
cloudWatchDestination: {
dimensionConfigurations: [{
defaultDimensionValue: "example",
dimensionName: "example",
dimensionValueSource: "MESSAGE_TAG",
}],
},
enabled: true,
matchingEventTypes: ["SEND"],
},
});
import pulumi
import pulumi_aws as aws
example = aws.sesv2.ConfigurationSet("example", configuration_set_name="example")
example_configuration_set_event_destination = aws.sesv2.ConfigurationSetEventDestination("example",
configuration_set_name=example.configuration_set_name,
event_destination_name="example",
event_destination={
"cloud_watch_destination": {
"dimension_configurations": [{
"default_dimension_value": "example",
"dimension_name": "example",
"dimension_value_source": "MESSAGE_TAG",
}],
},
"enabled": True,
"matching_event_types": ["SEND"],
})
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/sesv2"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
example, err := sesv2.NewConfigurationSet(ctx, "example", &sesv2.ConfigurationSetArgs{
ConfigurationSetName: pulumi.String("example"),
})
if err != nil {
return err
}
_, err = sesv2.NewConfigurationSetEventDestination(ctx, "example", &sesv2.ConfigurationSetEventDestinationArgs{
ConfigurationSetName: example.ConfigurationSetName,
EventDestinationName: pulumi.String("example"),
EventDestination: &sesv2.ConfigurationSetEventDestinationEventDestinationArgs{
CloudWatchDestination: &sesv2.ConfigurationSetEventDestinationEventDestinationCloudWatchDestinationArgs{
DimensionConfigurations: sesv2.ConfigurationSetEventDestinationEventDestinationCloudWatchDestinationDimensionConfigurationArray{
&sesv2.ConfigurationSetEventDestinationEventDestinationCloudWatchDestinationDimensionConfigurationArgs{
DefaultDimensionValue: pulumi.String("example"),
DimensionName: pulumi.String("example"),
DimensionValueSource: pulumi.String("MESSAGE_TAG"),
},
},
},
Enabled: pulumi.Bool(true),
MatchingEventTypes: pulumi.StringArray{
pulumi.String("SEND"),
},
},
})
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 example = new Aws.SesV2.ConfigurationSet("example", new()
{
ConfigurationSetName = "example",
});
var exampleConfigurationSetEventDestination = new Aws.SesV2.ConfigurationSetEventDestination("example", new()
{
ConfigurationSetName = example.ConfigurationSetName,
EventDestinationName = "example",
EventDestination = new Aws.SesV2.Inputs.ConfigurationSetEventDestinationEventDestinationArgs
{
CloudWatchDestination = new Aws.SesV2.Inputs.ConfigurationSetEventDestinationEventDestinationCloudWatchDestinationArgs
{
DimensionConfigurations = new[]
{
new Aws.SesV2.Inputs.ConfigurationSetEventDestinationEventDestinationCloudWatchDestinationDimensionConfigurationArgs
{
DefaultDimensionValue = "example",
DimensionName = "example",
DimensionValueSource = "MESSAGE_TAG",
},
},
},
Enabled = true,
MatchingEventTypes = new[]
{
"SEND",
},
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.sesv2.ConfigurationSet;
import com.pulumi.aws.sesv2.ConfigurationSetArgs;
import com.pulumi.aws.sesv2.ConfigurationSetEventDestination;
import com.pulumi.aws.sesv2.ConfigurationSetEventDestinationArgs;
import com.pulumi.aws.sesv2.inputs.ConfigurationSetEventDestinationEventDestinationArgs;
import com.pulumi.aws.sesv2.inputs.ConfigurationSetEventDestinationEventDestinationCloudWatchDestinationArgs;
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 example = new ConfigurationSet("example", ConfigurationSetArgs.builder()
.configurationSetName("example")
.build());
var exampleConfigurationSetEventDestination = new ConfigurationSetEventDestination("exampleConfigurationSetEventDestination", ConfigurationSetEventDestinationArgs.builder()
.configurationSetName(example.configurationSetName())
.eventDestinationName("example")
.eventDestination(ConfigurationSetEventDestinationEventDestinationArgs.builder()
.cloudWatchDestination(ConfigurationSetEventDestinationEventDestinationCloudWatchDestinationArgs.builder()
.dimensionConfigurations(ConfigurationSetEventDestinationEventDestinationCloudWatchDestinationDimensionConfigurationArgs.builder()
.defaultDimensionValue("example")
.dimensionName("example")
.dimensionValueSource("MESSAGE_TAG")
.build())
.build())
.enabled(true)
.matchingEventTypes("SEND")
.build())
.build());
}
}
resources:
example:
type: aws:sesv2:ConfigurationSet
properties:
configurationSetName: example
exampleConfigurationSetEventDestination:
type: aws:sesv2:ConfigurationSetEventDestination
name: example
properties:
configurationSetName: ${example.configurationSetName}
eventDestinationName: example
eventDestination:
cloudWatchDestination:
dimensionConfigurations:
- defaultDimensionValue: example
dimensionName: example
dimensionValueSource: MESSAGE_TAG
enabled: true
matchingEventTypes:
- SEND
When SES processes emails, it publishes events to CloudWatch based on matchingEventTypes. The dimensionConfigurations array defines how to group metrics; dimensionValueSource determines where dimension values come from (MESSAGE_TAG extracts from email headers). This creates CloudWatch metrics you can query and visualize.
Stream email events to S3 or Redshift via Firehose
Applications needing long-term storage or analytics route email events through Kinesis Firehose.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const example = new aws.sesv2.ConfigurationSet("example", {configurationSetName: "example"});
const exampleConfigurationSetEventDestination = new aws.sesv2.ConfigurationSetEventDestination("example", {
configurationSetName: example.configurationSetName,
eventDestinationName: "example",
eventDestination: {
kinesisFirehoseDestination: {
deliveryStreamArn: exampleAwsKinesisFirehoseDeliveryStream.arn,
iamRoleArn: exampleAwsIamRole.arn,
},
enabled: true,
matchingEventTypes: ["SEND"],
},
});
import pulumi
import pulumi_aws as aws
example = aws.sesv2.ConfigurationSet("example", configuration_set_name="example")
example_configuration_set_event_destination = aws.sesv2.ConfigurationSetEventDestination("example",
configuration_set_name=example.configuration_set_name,
event_destination_name="example",
event_destination={
"kinesis_firehose_destination": {
"delivery_stream_arn": example_aws_kinesis_firehose_delivery_stream["arn"],
"iam_role_arn": example_aws_iam_role["arn"],
},
"enabled": True,
"matching_event_types": ["SEND"],
})
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/sesv2"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
example, err := sesv2.NewConfigurationSet(ctx, "example", &sesv2.ConfigurationSetArgs{
ConfigurationSetName: pulumi.String("example"),
})
if err != nil {
return err
}
_, err = sesv2.NewConfigurationSetEventDestination(ctx, "example", &sesv2.ConfigurationSetEventDestinationArgs{
ConfigurationSetName: example.ConfigurationSetName,
EventDestinationName: pulumi.String("example"),
EventDestination: &sesv2.ConfigurationSetEventDestinationEventDestinationArgs{
KinesisFirehoseDestination: &sesv2.ConfigurationSetEventDestinationEventDestinationKinesisFirehoseDestinationArgs{
DeliveryStreamArn: pulumi.Any(exampleAwsKinesisFirehoseDeliveryStream.Arn),
IamRoleArn: pulumi.Any(exampleAwsIamRole.Arn),
},
Enabled: pulumi.Bool(true),
MatchingEventTypes: pulumi.StringArray{
pulumi.String("SEND"),
},
},
})
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 example = new Aws.SesV2.ConfigurationSet("example", new()
{
ConfigurationSetName = "example",
});
var exampleConfigurationSetEventDestination = new Aws.SesV2.ConfigurationSetEventDestination("example", new()
{
ConfigurationSetName = example.ConfigurationSetName,
EventDestinationName = "example",
EventDestination = new Aws.SesV2.Inputs.ConfigurationSetEventDestinationEventDestinationArgs
{
KinesisFirehoseDestination = new Aws.SesV2.Inputs.ConfigurationSetEventDestinationEventDestinationKinesisFirehoseDestinationArgs
{
DeliveryStreamArn = exampleAwsKinesisFirehoseDeliveryStream.Arn,
IamRoleArn = exampleAwsIamRole.Arn,
},
Enabled = true,
MatchingEventTypes = new[]
{
"SEND",
},
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.sesv2.ConfigurationSet;
import com.pulumi.aws.sesv2.ConfigurationSetArgs;
import com.pulumi.aws.sesv2.ConfigurationSetEventDestination;
import com.pulumi.aws.sesv2.ConfigurationSetEventDestinationArgs;
import com.pulumi.aws.sesv2.inputs.ConfigurationSetEventDestinationEventDestinationArgs;
import com.pulumi.aws.sesv2.inputs.ConfigurationSetEventDestinationEventDestinationKinesisFirehoseDestinationArgs;
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 example = new ConfigurationSet("example", ConfigurationSetArgs.builder()
.configurationSetName("example")
.build());
var exampleConfigurationSetEventDestination = new ConfigurationSetEventDestination("exampleConfigurationSetEventDestination", ConfigurationSetEventDestinationArgs.builder()
.configurationSetName(example.configurationSetName())
.eventDestinationName("example")
.eventDestination(ConfigurationSetEventDestinationEventDestinationArgs.builder()
.kinesisFirehoseDestination(ConfigurationSetEventDestinationEventDestinationKinesisFirehoseDestinationArgs.builder()
.deliveryStreamArn(exampleAwsKinesisFirehoseDeliveryStream.arn())
.iamRoleArn(exampleAwsIamRole.arn())
.build())
.enabled(true)
.matchingEventTypes("SEND")
.build())
.build());
}
}
resources:
example:
type: aws:sesv2:ConfigurationSet
properties:
configurationSetName: example
exampleConfigurationSetEventDestination:
type: aws:sesv2:ConfigurationSetEventDestination
name: example
properties:
configurationSetName: ${example.configurationSetName}
eventDestinationName: example
eventDestination:
kinesisFirehoseDestination:
deliveryStreamArn: ${exampleAwsKinesisFirehoseDeliveryStream.arn}
iamRoleArn: ${exampleAwsIamRole.arn}
enabled: true
matchingEventTypes:
- SEND
SES publishes events to the Firehose delivery stream, which batches and delivers them to S3, Redshift, or other destinations. The iamRoleArn grants SES permission to write to Firehose. This pattern supports compliance archival and feeding analytics pipelines.
Publish email events to SNS topics for notifications
Real-time alerting workflows publish SES events to SNS topics for immediate action.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const example = new aws.sesv2.ConfigurationSet("example", {configurationSetName: "example"});
const exampleConfigurationSetEventDestination = new aws.sesv2.ConfigurationSetEventDestination("example", {
configurationSetName: example.configurationSetName,
eventDestinationName: "example",
eventDestination: {
snsDestination: {
topicArn: exampleAwsSnsTopic.arn,
},
enabled: true,
matchingEventTypes: ["SEND"],
},
});
import pulumi
import pulumi_aws as aws
example = aws.sesv2.ConfigurationSet("example", configuration_set_name="example")
example_configuration_set_event_destination = aws.sesv2.ConfigurationSetEventDestination("example",
configuration_set_name=example.configuration_set_name,
event_destination_name="example",
event_destination={
"sns_destination": {
"topic_arn": example_aws_sns_topic["arn"],
},
"enabled": True,
"matching_event_types": ["SEND"],
})
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/sesv2"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
example, err := sesv2.NewConfigurationSet(ctx, "example", &sesv2.ConfigurationSetArgs{
ConfigurationSetName: pulumi.String("example"),
})
if err != nil {
return err
}
_, err = sesv2.NewConfigurationSetEventDestination(ctx, "example", &sesv2.ConfigurationSetEventDestinationArgs{
ConfigurationSetName: example.ConfigurationSetName,
EventDestinationName: pulumi.String("example"),
EventDestination: &sesv2.ConfigurationSetEventDestinationEventDestinationArgs{
SnsDestination: &sesv2.ConfigurationSetEventDestinationEventDestinationSnsDestinationArgs{
TopicArn: pulumi.Any(exampleAwsSnsTopic.Arn),
},
Enabled: pulumi.Bool(true),
MatchingEventTypes: pulumi.StringArray{
pulumi.String("SEND"),
},
},
})
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 example = new Aws.SesV2.ConfigurationSet("example", new()
{
ConfigurationSetName = "example",
});
var exampleConfigurationSetEventDestination = new Aws.SesV2.ConfigurationSetEventDestination("example", new()
{
ConfigurationSetName = example.ConfigurationSetName,
EventDestinationName = "example",
EventDestination = new Aws.SesV2.Inputs.ConfigurationSetEventDestinationEventDestinationArgs
{
SnsDestination = new Aws.SesV2.Inputs.ConfigurationSetEventDestinationEventDestinationSnsDestinationArgs
{
TopicArn = exampleAwsSnsTopic.Arn,
},
Enabled = true,
MatchingEventTypes = new[]
{
"SEND",
},
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.sesv2.ConfigurationSet;
import com.pulumi.aws.sesv2.ConfigurationSetArgs;
import com.pulumi.aws.sesv2.ConfigurationSetEventDestination;
import com.pulumi.aws.sesv2.ConfigurationSetEventDestinationArgs;
import com.pulumi.aws.sesv2.inputs.ConfigurationSetEventDestinationEventDestinationArgs;
import com.pulumi.aws.sesv2.inputs.ConfigurationSetEventDestinationEventDestinationSnsDestinationArgs;
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 example = new ConfigurationSet("example", ConfigurationSetArgs.builder()
.configurationSetName("example")
.build());
var exampleConfigurationSetEventDestination = new ConfigurationSetEventDestination("exampleConfigurationSetEventDestination", ConfigurationSetEventDestinationArgs.builder()
.configurationSetName(example.configurationSetName())
.eventDestinationName("example")
.eventDestination(ConfigurationSetEventDestinationEventDestinationArgs.builder()
.snsDestination(ConfigurationSetEventDestinationEventDestinationSnsDestinationArgs.builder()
.topicArn(exampleAwsSnsTopic.arn())
.build())
.enabled(true)
.matchingEventTypes("SEND")
.build())
.build());
}
}
resources:
example:
type: aws:sesv2:ConfigurationSet
properties:
configurationSetName: example
exampleConfigurationSetEventDestination:
type: aws:sesv2:ConfigurationSetEventDestination
name: example
properties:
configurationSetName: ${example.configurationSetName}
eventDestinationName: example
eventDestination:
snsDestination:
topicArn: ${exampleAwsSnsTopic.arn}
enabled: true
matchingEventTypes:
- SEND
When events match the specified types, SES publishes to the SNS topic. Subscribers (Lambda functions, email endpoints, SQS queues) receive notifications immediately. This enables bounce handling, complaint processing, and delivery confirmations.
Beyond these examples
These snippets focus on specific event destination features: CloudWatch metrics and dimensions, Kinesis Firehose streaming, and SNS topic publishing. They’re intentionally minimal rather than full email monitoring solutions.
The examples reference pre-existing infrastructure such as SESv2 configuration sets, CloudWatch event buses, Kinesis Firehose streams, SNS topics, Pinpoint apps, and IAM roles with appropriate permissions. They focus on routing configuration rather than provisioning the destination services.
To keep things focused, common event destination patterns are omitted, including:
- Event filtering beyond matchingEventTypes
- Multiple destinations per configuration set
- EventBridge and Pinpoint integration patterns
- Dimension value sources (EMAIL_HEADER, LINK_TAG)
These omissions are intentional: the goal is to illustrate how each destination type is wired, not provide drop-in email monitoring modules. See the ConfigurationSetEventDestination resource reference for all available configuration options.
Let's configure AWS SESv2 Event Destinations
Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.
Try Pulumi Cloud for FREEFrequently Asked Questions
Destination Types & Configuration
dimensionConfigurations for custom metrics, while EventBridge destinations route events to an event bus using eventBusArn. Choose CloudWatch for metrics and EventBridge for event-driven workflows.deliveryStreamArn and iamRoleArn. The IAM role must have permissions to write to the delivery stream.Event Tracking & Monitoring
matchingEventTypes property specifies which events to track. All examples show SEND events, but other types like bounces, complaints, and deliveries are also available.ConfigurationSetEventDestination resources with different eventDestinationName values.Resource Management
configurationSetName and eventDestinationName are immutable. Changing either requires destroying and recreating the resource.configuration_set_name|event_destination_name for the import ID. For example: pulumi import aws:sesv2/configurationSetEventDestination:ConfigurationSetEventDestination example example_configuration_set|example_event_destination.Using a different cloud?
Explore integration guides for other cloud providers: