Configure AWS SESv2 Email Identities

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 FREE

Frequently Asked Questions

Identity Types & Setup
What's the difference between email address and domain identities?
Email address identities verify a single email (e.g., testing@example.com), while domain identities verify an entire domain (e.g., example.com). The identityType output shows EMAIL_ADDRESS or DOMAIN.
Can I change my email identity after creation?
No, the emailIdentity property is immutable. To change the email address or domain, you must destroy and recreate the resource.
DKIM Configuration
How do I configure BYODKIM (Bring Your Own DKIM)?
Set dkimSigningAttributes with domainSigningPrivateKey (your private key) and domainSigningSelector (your selector name).
Verification & Status
What do the verification statuses mean?
The verificationStatus can be PENDING (verification in progress), SUCCESS (verified), FAILED (verification failed), TEMPORARY_FAILURE (temporary issue), or NOT_STARTED (verification not initiated).
What's the difference between verificationStatus and verifiedForSendingStatus?
verificationStatus shows the detailed verification state (PENDING, SUCCESS, etc.), while verifiedForSendingStatus is a boolean indicating whether the identity can send emails.
Configuration & Integration
How do I link my identity to a configuration set?
Set configurationSetName to the name of your configuration set. This becomes the default for emails sent from this identity, but can be overridden per-request.
What region is my email identity created in?
The 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: