The aws:sesv2/emailIdentity:EmailIdentity resource, part of the Pulumi AWS provider, registers email addresses or domains with SES for sending, enabling verification and authentication. This guide focuses on three capabilities: email address vs domain verification, configuration set attachment, and custom DKIM signing.
Email identities require DNS access for domain verification and may reference configuration sets or DKIM keys that must exist separately. The examples are intentionally small. Combine them with your own DNS records, configuration sets, and authentication setup.
Verify an email address for sending
Teams testing SES or sending from individual addresses start by verifying a single email address, which allows immediate sending without DNS configuration.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const example = new aws.sesv2.EmailIdentity("example", {emailIdentity: "testing@example.com"});
import pulumi
import pulumi_aws as aws
example = aws.sesv2.EmailIdentity("example", email_identity="testing@example.com")
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 {
_, err := sesv2.NewEmailIdentity(ctx, "example", &sesv2.EmailIdentityArgs{
EmailIdentity: pulumi.String("testing@example.com"),
})
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.EmailIdentity("example", new()
{
EmailIdentityDetails = "testing@example.com",
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.sesv2.EmailIdentity;
import com.pulumi.aws.sesv2.EmailIdentityArgs;
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 EmailIdentity("example", EmailIdentityArgs.builder()
.emailIdentity("testing@example.com")
.build());
}
}
resources:
example:
type: aws:sesv2:EmailIdentity
properties:
emailIdentity: testing@example.com
The emailIdentity property accepts an email address, and SES sends a verification email to that address. Once verified, you can send from this specific address. The identityType output shows EMAIL_ADDRESS for this configuration.
Verify a domain for sending
Production deployments typically verify entire domains, allowing any address under that domain to send without individual verification.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const example = new aws.sesv2.EmailIdentity("example", {emailIdentity: "example.com"});
import pulumi
import pulumi_aws as aws
example = aws.sesv2.EmailIdentity("example", email_identity="example.com")
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 {
_, err := sesv2.NewEmailIdentity(ctx, "example", &sesv2.EmailIdentityArgs{
EmailIdentity: pulumi.String("example.com"),
})
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.EmailIdentity("example", new()
{
EmailIdentityDetails = "example.com",
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.sesv2.EmailIdentity;
import com.pulumi.aws.sesv2.EmailIdentityArgs;
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 EmailIdentity("example", EmailIdentityArgs.builder()
.emailIdentity("example.com")
.build());
}
}
resources:
example:
type: aws:sesv2:EmailIdentity
properties:
emailIdentity: example.com
When emailIdentity contains a domain name, SES generates DNS verification records you must publish. Once verified, any address under the domain can send. The identityType output shows DOMAIN for this configuration.
Route emails through a configuration set
Applications that need consistent tracking, reputation management, or event publishing attach identities to configuration sets.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const example = new aws.sesv2.ConfigurationSet("example", {configurationSetName: "example"});
const exampleEmailIdentity = new aws.sesv2.EmailIdentity("example", {
emailIdentity: "example.com",
configurationSetName: example.configurationSetName,
});
import pulumi
import pulumi_aws as aws
example = aws.sesv2.ConfigurationSet("example", configuration_set_name="example")
example_email_identity = aws.sesv2.EmailIdentity("example",
email_identity="example.com",
configuration_set_name=example.configuration_set_name)
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.NewEmailIdentity(ctx, "example", &sesv2.EmailIdentityArgs{
EmailIdentity: pulumi.String("example.com"),
ConfigurationSetName: example.ConfigurationSetName,
})
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 exampleEmailIdentity = new Aws.SesV2.EmailIdentity("example", new()
{
EmailIdentityDetails = "example.com",
ConfigurationSetName = example.ConfigurationSetName,
});
});
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.EmailIdentity;
import com.pulumi.aws.sesv2.EmailIdentityArgs;
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 exampleEmailIdentity = new EmailIdentity("exampleEmailIdentity", EmailIdentityArgs.builder()
.emailIdentity("example.com")
.configurationSetName(example.configurationSetName())
.build());
}
}
resources:
example:
type: aws:sesv2:ConfigurationSet
properties:
configurationSetName: example
exampleEmailIdentity:
type: aws:sesv2:EmailIdentity
name: example
properties:
emailIdentity: example.com
configurationSetName: ${example.configurationSetName}
The configurationSetName property links this identity to a configuration set, applying its tracking and event rules to all emails sent from this identity. The configuration set must exist before the identity references it.
Sign emails with your own DKIM keys
Organizations with existing DKIM infrastructure can bring their own keys rather than using AWS-managed signing.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const example = new aws.sesv2.EmailIdentity("example", {
emailIdentity: "example.com",
dkimSigningAttributes: {
domainSigningPrivateKey: "MIIJKAIBAAKCAgEA2Se7p8zvnI4yh+Gh9j2rG5e2aRXjg03Y8saiupLnadPH9xvM...",
domainSigningSelector: "example",
},
});
import pulumi
import pulumi_aws as aws
example = aws.sesv2.EmailIdentity("example",
email_identity="example.com",
dkim_signing_attributes={
"domain_signing_private_key": "MIIJKAIBAAKCAgEA2Se7p8zvnI4yh+Gh9j2rG5e2aRXjg03Y8saiupLnadPH9xvM...",
"domain_signing_selector": "example",
})
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 {
_, err := sesv2.NewEmailIdentity(ctx, "example", &sesv2.EmailIdentityArgs{
EmailIdentity: pulumi.String("example.com"),
DkimSigningAttributes: &sesv2.EmailIdentityDkimSigningAttributesArgs{
DomainSigningPrivateKey: pulumi.String("MIIJKAIBAAKCAgEA2Se7p8zvnI4yh+Gh9j2rG5e2aRXjg03Y8saiupLnadPH9xvM..."),
DomainSigningSelector: pulumi.String("example"),
},
})
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.EmailIdentity("example", new()
{
EmailIdentityDetails = "example.com",
DkimSigningAttributes = new Aws.SesV2.Inputs.EmailIdentityDkimSigningAttributesArgs
{
DomainSigningPrivateKey = "MIIJKAIBAAKCAgEA2Se7p8zvnI4yh+Gh9j2rG5e2aRXjg03Y8saiupLnadPH9xvM...",
DomainSigningSelector = "example",
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.sesv2.EmailIdentity;
import com.pulumi.aws.sesv2.EmailIdentityArgs;
import com.pulumi.aws.sesv2.inputs.EmailIdentityDkimSigningAttributesArgs;
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 EmailIdentity("example", EmailIdentityArgs.builder()
.emailIdentity("example.com")
.dkimSigningAttributes(EmailIdentityDkimSigningAttributesArgs.builder()
.domainSigningPrivateKey("MIIJKAIBAAKCAgEA2Se7p8zvnI4yh+Gh9j2rG5e2aRXjg03Y8saiupLnadPH9xvM...")
.domainSigningSelector("example")
.build())
.build());
}
}
resources:
example:
type: aws:sesv2:EmailIdentity
properties:
emailIdentity: example.com
dkimSigningAttributes:
domainSigningPrivateKey: MIIJKAIBAAKCAgEA2Se7p8zvnI4yh+Gh9j2rG5e2aRXjg03Y8saiupLnadPH9xvM...
domainSigningSelector: example
The dkimSigningAttributes block configures custom DKIM signing. The domainSigningPrivateKey contains your private key, and domainSigningSelector specifies the DNS selector where your public key is published. You must publish the corresponding public key to DNS separately.
Beyond these examples
These snippets focus on specific identity-level features: email address and domain verification, configuration set attachment, and custom DKIM signing. They’re intentionally minimal rather than full email infrastructure.
The examples may reference pre-existing infrastructure such as DNS access for domain verification records, configuration sets for routing, and DKIM key pairs for BYODKIM. They focus on registering the identity rather than provisioning DNS or generating keys.
To keep things focused, common identity patterns are omitted, including:
- DNS record creation for domain verification
- DKIM key generation and DNS publishing
- Verification status monitoring (verificationStatus)
- Regional identity management (region property)
These omissions are intentional: the goal is to illustrate how each identity feature is wired, not provide drop-in email modules. See the SESv2 EmailIdentity resource reference for all available configuration options.
Let's configure AWS SESv2 Email Identities
Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.
Try Pulumi Cloud for FREEFrequently Asked Questions
Identity Types & Setup
testing@example.com), while domain identities verify an entire domain (e.g., example.com). The identityType output shows EMAIL_ADDRESS or DOMAIN.emailIdentity property is immutable. To change the email address or domain, you must destroy and recreate the resource.DKIM Configuration
dkimSigningAttributes with domainSigningPrivateKey (your private key) and domainSigningSelector (your selector name).Verification & Status
verificationStatus can be PENDING (verification in progress), SUCCESS (verified), FAILED (verification failed), TEMPORARY_FAILURE (temporary issue), or NOT_STARTED (verification not initiated).verificationStatus shows the detailed verification state (PENDING, SUCCESS, etc.), while verifiedForSendingStatus is a boolean indicating whether the identity can send emails.Configuration & Integration
configurationSetName to the name of your configuration set. This becomes the default for emails sent from this identity, but can be overridden per-request.region property defaults to the region configured in your provider. You can override it by explicitly setting region.Using a different cloud?
Explore integration guides for other cloud providers: