The aws:ses/eventDestination:EventDestination resource, part of the Pulumi AWS provider, routes SES email events (bounces, deliveries, opens, clicks) to CloudWatch, Kinesis Firehose, or SNS. This guide focuses on three capabilities: CloudWatch metrics integration, Kinesis Firehose streaming, and SNS topic publishing.
Event destinations belong to SES configuration sets and route events to existing CloudWatch namespaces, Kinesis streams, or SNS topics. The examples are intentionally small. Combine them with your own configuration sets and destination infrastructure.
Send email events to CloudWatch for metrics
Teams monitoring email delivery route SES events to CloudWatch to track bounce rates, send volumes, and delivery patterns as custom metrics.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const cloudwatch = new aws.ses.EventDestination("cloudwatch", {
name: "event-destination-cloudwatch",
configurationSetName: example.name,
enabled: true,
matchingTypes: [
"bounce",
"send",
],
cloudwatchDestinations: [{
defaultValue: "default",
dimensionName: "dimension",
valueSource: "emailHeader",
}],
});
import pulumi
import pulumi_aws as aws
cloudwatch = aws.ses.EventDestination("cloudwatch",
name="event-destination-cloudwatch",
configuration_set_name=example["name"],
enabled=True,
matching_types=[
"bounce",
"send",
],
cloudwatch_destinations=[{
"default_value": "default",
"dimension_name": "dimension",
"value_source": "emailHeader",
}])
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/ses"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := ses.NewEventDestination(ctx, "cloudwatch", &ses.EventDestinationArgs{
Name: pulumi.String("event-destination-cloudwatch"),
ConfigurationSetName: pulumi.Any(example.Name),
Enabled: pulumi.Bool(true),
MatchingTypes: pulumi.StringArray{
pulumi.String("bounce"),
pulumi.String("send"),
},
CloudwatchDestinations: ses.EventDestinationCloudwatchDestinationArray{
&ses.EventDestinationCloudwatchDestinationArgs{
DefaultValue: pulumi.String("default"),
DimensionName: pulumi.String("dimension"),
ValueSource: pulumi.String("emailHeader"),
},
},
})
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 cloudwatch = new Aws.Ses.EventDestination("cloudwatch", new()
{
Name = "event-destination-cloudwatch",
ConfigurationSetName = example.Name,
Enabled = true,
MatchingTypes = new[]
{
"bounce",
"send",
},
CloudwatchDestinations = new[]
{
new Aws.Ses.Inputs.EventDestinationCloudwatchDestinationArgs
{
DefaultValue = "default",
DimensionName = "dimension",
ValueSource = "emailHeader",
},
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.ses.EventDestination;
import com.pulumi.aws.ses.EventDestinationArgs;
import com.pulumi.aws.ses.inputs.EventDestinationCloudwatchDestinationArgs;
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 cloudwatch = new EventDestination("cloudwatch", EventDestinationArgs.builder()
.name("event-destination-cloudwatch")
.configurationSetName(example.name())
.enabled(true)
.matchingTypes(
"bounce",
"send")
.cloudwatchDestinations(EventDestinationCloudwatchDestinationArgs.builder()
.defaultValue("default")
.dimensionName("dimension")
.valueSource("emailHeader")
.build())
.build());
}
}
resources:
cloudwatch:
type: aws:ses:EventDestination
properties:
name: event-destination-cloudwatch
configurationSetName: ${example.name}
enabled: true
matchingTypes:
- bounce
- send
cloudwatchDestinations:
- defaultValue: default
dimensionName: dimension
valueSource: emailHeader
When SES processes emails, it evaluates the matchingTypes filter and sends matching events to CloudWatch. The cloudwatchDestinations property defines custom metric dimensions; dimensionName sets the metric dimension name, and valueSource specifies where to extract the dimension value (here, from email headers). This enables dashboards and alarms based on email behavior.
Stream email events to Kinesis Firehose
Applications that need to analyze email events at scale or archive them to S3 route events through Kinesis Firehose.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const kinesis = new aws.ses.EventDestination("kinesis", {
name: "event-destination-kinesis",
configurationSetName: exampleAwsSesConfigurationSet.name,
enabled: true,
matchingTypes: [
"bounce",
"send",
],
kinesisDestination: {
streamArn: exampleAwsKinesisFirehoseDeliveryStream.arn,
roleArn: example.arn,
},
});
import pulumi
import pulumi_aws as aws
kinesis = aws.ses.EventDestination("kinesis",
name="event-destination-kinesis",
configuration_set_name=example_aws_ses_configuration_set["name"],
enabled=True,
matching_types=[
"bounce",
"send",
],
kinesis_destination={
"stream_arn": example_aws_kinesis_firehose_delivery_stream["arn"],
"role_arn": example["arn"],
})
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/ses"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := ses.NewEventDestination(ctx, "kinesis", &ses.EventDestinationArgs{
Name: pulumi.String("event-destination-kinesis"),
ConfigurationSetName: pulumi.Any(exampleAwsSesConfigurationSet.Name),
Enabled: pulumi.Bool(true),
MatchingTypes: pulumi.StringArray{
pulumi.String("bounce"),
pulumi.String("send"),
},
KinesisDestination: &ses.EventDestinationKinesisDestinationArgs{
StreamArn: pulumi.Any(exampleAwsKinesisFirehoseDeliveryStream.Arn),
RoleArn: pulumi.Any(example.Arn),
},
})
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 kinesis = new Aws.Ses.EventDestination("kinesis", new()
{
Name = "event-destination-kinesis",
ConfigurationSetName = exampleAwsSesConfigurationSet.Name,
Enabled = true,
MatchingTypes = new[]
{
"bounce",
"send",
},
KinesisDestination = new Aws.Ses.Inputs.EventDestinationKinesisDestinationArgs
{
StreamArn = exampleAwsKinesisFirehoseDeliveryStream.Arn,
RoleArn = example.Arn,
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.ses.EventDestination;
import com.pulumi.aws.ses.EventDestinationArgs;
import com.pulumi.aws.ses.inputs.EventDestinationKinesisDestinationArgs;
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 kinesis = new EventDestination("kinesis", EventDestinationArgs.builder()
.name("event-destination-kinesis")
.configurationSetName(exampleAwsSesConfigurationSet.name())
.enabled(true)
.matchingTypes(
"bounce",
"send")
.kinesisDestination(EventDestinationKinesisDestinationArgs.builder()
.streamArn(exampleAwsKinesisFirehoseDeliveryStream.arn())
.roleArn(example.arn())
.build())
.build());
}
}
resources:
kinesis:
type: aws:ses:EventDestination
properties:
name: event-destination-kinesis
configurationSetName: ${exampleAwsSesConfigurationSet.name}
enabled: true
matchingTypes:
- bounce
- send
kinesisDestination:
streamArn: ${exampleAwsKinesisFirehoseDeliveryStream.arn}
roleArn: ${example.arn}
The kinesisDestination property sends events to a Firehose stream for transformation and delivery. The streamArn identifies the destination stream, and roleArn grants SES permission to write records. Firehose can then deliver events to S3, Redshift, or other analytics systems.
Publish email events to SNS topics
Systems that need real-time notifications publish to SNS topics, enabling Lambda functions, SQS queues, or HTTP endpoints to react immediately.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const sns = new aws.ses.EventDestination("sns", {
name: "event-destination-sns",
configurationSetName: exampleAwsSesConfigurationSet.name,
enabled: true,
matchingTypes: [
"bounce",
"send",
],
snsDestination: {
topicArn: example.arn,
},
});
import pulumi
import pulumi_aws as aws
sns = aws.ses.EventDestination("sns",
name="event-destination-sns",
configuration_set_name=example_aws_ses_configuration_set["name"],
enabled=True,
matching_types=[
"bounce",
"send",
],
sns_destination={
"topic_arn": example["arn"],
})
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/ses"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := ses.NewEventDestination(ctx, "sns", &ses.EventDestinationArgs{
Name: pulumi.String("event-destination-sns"),
ConfigurationSetName: pulumi.Any(exampleAwsSesConfigurationSet.Name),
Enabled: pulumi.Bool(true),
MatchingTypes: pulumi.StringArray{
pulumi.String("bounce"),
pulumi.String("send"),
},
SnsDestination: &ses.EventDestinationSnsDestinationArgs{
TopicArn: pulumi.Any(example.Arn),
},
})
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 sns = new Aws.Ses.EventDestination("sns", new()
{
Name = "event-destination-sns",
ConfigurationSetName = exampleAwsSesConfigurationSet.Name,
Enabled = true,
MatchingTypes = new[]
{
"bounce",
"send",
},
SnsDestination = new Aws.Ses.Inputs.EventDestinationSnsDestinationArgs
{
TopicArn = example.Arn,
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.ses.EventDestination;
import com.pulumi.aws.ses.EventDestinationArgs;
import com.pulumi.aws.ses.inputs.EventDestinationSnsDestinationArgs;
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 sns = new EventDestination("sns", EventDestinationArgs.builder()
.name("event-destination-sns")
.configurationSetName(exampleAwsSesConfigurationSet.name())
.enabled(true)
.matchingTypes(
"bounce",
"send")
.snsDestination(EventDestinationSnsDestinationArgs.builder()
.topicArn(example.arn())
.build())
.build());
}
}
resources:
sns:
type: aws:ses:EventDestination
properties:
name: event-destination-sns
configurationSetName: ${exampleAwsSesConfigurationSet.name}
enabled: true
matchingTypes:
- bounce
- send
snsDestination:
topicArn: ${example.arn}
The snsDestination property publishes events to an SNS topic. The topicArn identifies the destination topic. Subscribers receive event payloads in real time, enabling immediate processing of bounces, complaints, or delivery confirmations.
Beyond these examples
These snippets focus on specific event destination features: CloudWatch metrics integration, 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 SES configuration sets, CloudWatch metric namespaces, Kinesis Firehose streams, or SNS topics, and IAM roles with appropriate permissions. They focus on routing configuration rather than provisioning the destination infrastructure.
To keep things focused, common event destination patterns are omitted, including:
- Event filtering beyond matchingTypes (no conditional routing)
- Multiple destinations per event type (mutually exclusive)
- Destination-specific error handling
- Event payload transformation
These omissions are intentional: the goal is to illustrate how each destination type is wired, not provide drop-in monitoring modules. See the SES EventDestination resource reference for all available configuration options.
Let's configure AWS SES 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 Configuration
cloudwatchDestinations), Kinesis Firehose (kinesisDestination), or SNS (snsDestination).cloudwatchDestinations or kinesisDestination, but not both. SNS destinations can be configured independently.Event Types & Matching
send, reject, bounce, complaint, delivery, open, click, and renderingFailure.Immutability & Limitations
configurationSetName, matchingTypes, name, enabled, and all destination configurations (cloudwatchDestinations, kinesisDestination, snsDestination).Using a different cloud?
Explore monitoring guides for other cloud providers: