The aws:sesv2/emailIdentity:EmailIdentity resource, part of the Pulumi AWS provider, registers email addresses or domains with SES for sending, enabling verification and DKIM configuration. This guide focuses on three capabilities: email address vs domain verification, configuration set attachment, and custom DKIM key configuration.
Email identities require DNS access for domain verification and DKIM records. Configuration sets must be created separately before attachment. The examples are intentionally small. Combine them with your own DNS configuration and sending policies.
Verify an email address for sending
Teams testing SES or sending from individual addresses start by verifying email addresses. AWS sends a verification email to confirm ownership.
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. After creation, AWS sends a verification email to that address. The verificationStatus output tracks the verification state: PENDING until the recipient clicks the link, then SUCCESS. Only verified identities can send email.
Verify a domain for sending
Production systems verify entire domains rather than individual addresses, allowing any address at that domain to send once DNS records are configured.
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 (no @ symbol), SES creates a domain identity. The identityType output shows DOMAIN. You must add DNS TXT records to prove domain ownership; verification remains PENDING until DNS propagates.
Route emails through a configuration set
Configuration sets define sending behavior like event destinations and IP pools. Attaching one to an identity applies those settings to all emails from that identity.
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 the identity to a ConfigurationSet resource. All emails from this identity inherit the configuration set’s policies unless overridden in the send request. Create the ConfigurationSet first, then reference its name.
Sign emails with custom DKIM keys
Organizations with existing DKIM infrastructure can bring their own keys rather than using AWS-managed DKIM, maintaining consistent signatures across providers.
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 BYODKIM (Bring Your Own DKIM). The domainSigningPrivateKey holds your RSA private key; domainSigningSelector specifies the DNS selector name. You must publish corresponding CNAME records in DNS for receiving servers to validate signatures.
Beyond these examples
These snippets focus on specific email identity features: email address and domain verification, configuration set attachment, and custom DKIM signing. They’re intentionally minimal rather than complete email sending systems.
The examples may reference pre-existing infrastructure such as DNS access for domain verification and DKIM records, ConfigurationSet resources, and DKIM key pairs for BYODKIM. They focus on identity registration rather than DNS or key management.
To keep things focused, common identity patterns are omitted, including:
- DNS record creation for verification
- Easy DKIM (AWS-managed keys)
- Tags for resource organization
- Region-specific identity management
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 & Verification
testing@example.com) and domains (like example.com). The identityType output will be EMAIL_ADDRESS or DOMAIN accordingly.verificationStatus output, which can be PENDING, SUCCESS, FAILED, TEMPORARY_FAILURE, or NOT_STARTED. The verifiedForSendingStatus boolean indicates whether the identity is ready for sending.verificationStatus shows the detailed verification state (like PENDING or SUCCESS), while verifiedForSendingStatus is a simple boolean indicating whether you can send emails from this identity.DKIM Configuration
dkimSigningAttributes with your domainSigningPrivateKey and domainSigningSelector to bring your own DKIM (BYODKIM).dkimSigningAttributes: domainSigningPrivateKey (your private key) and domainSigningSelector (your selector name).Configuration & Settings
emailIdentity is immutable. Changing it will force replacement of the resource.configurationSetName property to specify a default configuration set. Note that any configuration set specified in the email sending request will override this default.region property, which defaults to the region configured in your provider.Using a different cloud?
Explore integration guides for other cloud providers: