Guide
Connect your AWS SES account with an IAM role
BYOC sends through your own AWS SES account. This guide creates the IAM role SendFleet assumes, explains what the ExternalId condition is for, and gives you the permissions policy to attach. About ten minutes, and nothing here sends a test email.
What you are creating
One IAM role in your AWS account, with two policies attached:
- A trust policy, which says who is allowed to assume the role. SendFleet is the only party, and only with the right ExternalId.
- A permissions policy, which says what SendFleet may do once it has the role. Eleven SES actions and nothing else.
Your AWS credentials are never shared with SendFleet and never appear in SendFleet's systems. SendFleet assumes the role for the length of a send, using a short-lived session.
Step 1: create the role
Open the role creator
In the AWS console go to IAM → Roles → Create role, then choose Custom trust policy rather than any of the listed service options.Name it SendFleet-BYOC-Role
The role name must be exactlySendFleet-BYOC-Role, case sensitive. SendFleet only assumes roles with that name, so a different name will validate and then fail to connect.Paste the trust policy
Use the copy at Dashboard → Settings → BYOC, not the template below. See the note under the sample.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::<SENDFLEET_ACCOUNT_ID>:root"
},
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": {
"sts:ExternalId": "<YOUR_EXTERNAL_ID>"
}
}
}
]
}Pasted as it stands this will not work: the two angle-bracket values are placeholders. The version in your dashboard has both of them filled in.
What the ExternalId is for
The sts:ExternalId condition is what stops anyone else from borrowing your role.
A role ARN is not a secret. If yours leaks, anyone who finds it can try to assume the role directly against your account. Without the condition, that attempt would succeed if the trust policy granted it. This is the confused deputy problem: a party that is trusted to act on your behalf being talked into acting for somebody else.
With the condition in place, an assume-role call only succeeds if it also carries the exact ExternalId that was generated for your account. SendFleet holds that value and nobody else does, so a leaked ARN is not enough. Keeping the condition costs you nothing and closes the hole.
Step 2: attach the permissions policy
Attach a permissions policy to the role, either as an inline policy or as a customer-managed one. These are the only eleven actions SendFleet uses.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "SendFleetSESPermissions",
"Effect": "Allow",
"Action": [
"ses:SendEmail",
"ses:SendRawEmail",
"ses:VerifyDomainIdentity",
"ses:VerifyDomainDkim",
"ses:GetIdentityVerificationAttributes",
"ses:GetIdentityDkimAttributes",
"ses:GetIdentityMailFromDomainAttributes",
"ses:SetIdentityMailFromDomain",
"ses:DeleteIdentity",
"ses:GetAccountSendingEnabled",
"ses:GetSendQuota"
],
"Resource": "*"
}
]
}What each permission is for
ses:SendEmail,ses:SendRawEmail: delivering your email.SendRawEmailis the one that carries attachments.ses:VerifyDomainIdentity,ses:VerifyDomainDkim: registering a sending domain and asking for its DKIM records.ses:GetIdentityVerificationAttributes,ses:GetIdentityDkimAttributes: reading back the verification state so the dashboard can show you whether it passed.ses:GetIdentityMailFromDomainAttributes,ses:SetIdentityMailFromDomain: configuring and checking the custom MAIL FROM subdomain.ses:DeleteIdentity: removing the SES identity when you delete a domain from SendFleet.ses:GetAccountSendingEnabled,ses:GetSendQuota: confirming that sending is enabled and that you are out of the sandbox.
Step 3: connect it
Open the BYOC settings
Go to Dashboard → Settings → BYOC. The trust policy template with your values in it is on this page.Enter your role ARN and region
Paste the role ARN, which is thearn:aws:iam::…:role/SendFleet-BYOC-Rolevalue at the top of the role's detail page in the AWS console, and select the SES region your account uses in that same region list.Choose Validate & save
SaveFleet checks the connection before accepting it. A success here means the role exists, the trust policy is right and the permissions are attached.Add a sending domain
Once the connection validates, add a domain under Dashboard → Domains. It is registered in your own SES account and needs the DNS records covered in the DNS Records reference.
If validation fails
- The role name is not exactly SendFleet-BYOC-Role. Check for a trailing space, a different case, or a suffix someone added.
- The ExternalId does not match. It is case sensitive. Copy it again from the dashboard rather than retyping it.
- The permissions policy is on the user, not the role. It has to be attached to the role itself.
- The region does not match the identity's region. An SES identity lives in one region, and the role has to be able to reach that one.
- The account is still in the SES sandbox. Check the sending enabled state in SES itself.
Next
With the role connected and a domain added, the next two steps are the DNS records for that domain and then your first send. The DNS Records reference has every record and the one-request send guide has working samples in five languages.