This module accepts the description of mail redirects, and configures Cloudflare Mail Routing for incoming mail and AWS SES for outgoing mail.
The setup is described in detail here: https://bdolgov.blog/b/custom-email-domain-using-gmail/.
The module creates the following resources:
- For every configured domain:
- Enables Cloudflare Mail Routing.
- Creates an AWS SES identity.
- Verifies the identity ownership in AWS SES by creating required DNS records in Cloudflare.
- Configures the AWS SES identity to use a custom MAIL FROM domain and creates required records in Cloudflare.
- For every configured redirect address:
- Creates a Cloudflare mail redirect from the address to the specified recipient.
- Creates an AWS SES identity.
- For every unique configured recipient (meaining: redirect target):
- Creates an AWS SES configuration set, sets it as default for all identities that are redirecting to the recipient, and configures SNS email notifications about failures in this configuration set.
- Creates an AWS IAM user, restricts it to entities that are redirecting to the recipient, creates and access key, and outputs SMTP credentials associated with the key.
A minimal example main.tf
:
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 5.51"
}
cloudflare = {
source = "cloudflare/cloudflare"
version = ">= 4.33"
}
}
}
provider "aws" { region = "eu-north-1" }
provider "cloudflare" {}
module "mail_setup" {
source = "github.com/bdolgov/terraform-aws-ses-cloudflare-mail-setup"
redirects = {
"foo.com" = {
"admin" = "someone@gmail.com", # Redirect admin@foo.com to someone@gmail.com.
"postmaster" = "someone@gmail.com", # Redirect postmaster@foo.com to someone@gmail.com.
},
"yourlast.name" = {
"alex" = "someone-else@gmail.com", # Redirect alex@yourlast.name to someone-else@gmail.com.
"boris" = "someone@gmail.com", # Redirect boris@yourlastname.com to someone@gmail.com.
},
}
pgp_key = "<gpg --export | base64 -w0>"
}
output "smtp_accounts" {
value = module.mail_setup.smtp_accounts
sensitive = true
}
Put CLOUDFLARE_API_TOKEN
, AWS_ACCESS_KEY_ID
, and AWS_SECRET_ACCESS_KEY
into environment
variables, and run terraform init && terraform apply && terraform output -json smtp_accounts | jq
.
The module will output SMTP credentials for all recipients:
{
"someone@gmail.com": {
"encrypted_password": "...",
"password": null,
"username": "AKIA..."
},
"someone=else@gmail.com": {
"encrypted_password": "...",
"password": null,
"username": "AKIA..."
},
}
For more information about encrypted SMTP passwords, see modules/terraform-aws-ses-restricted-smtp-user/.
Name | Version |
---|---|
aws | >=5.51.0 |
cloudflare | >=4.33.0 |
Name | Version |
---|---|
aws | 5.50.0 |
cloudflare | 4.33.0 |
Name | Source | Version |
---|---|---|
dns_records | ./modules/terraform-cloudflare-aws-ses-records | n/a |
email_routing_address | ./modules/terraform-cloudflare-email-routing-address | n/a |
ses_sns_notification | ./modules/terraform-aws-ses-sns-notification | n/a |
smtp_user | ./modules/terraform-aws-ses-restricted-smtp-user | n/a |
Name | Type |
---|---|
aws_sesv2_email_identity.address | resource |
aws_sesv2_email_identity.domain | resource |
aws_sesv2_email_identity_feedback_attributes.identity | resource |
aws_sesv2_email_identity_mail_from_attributes.address | resource |
aws_sesv2_email_identity_mail_from_attributes.domain | resource |
cloudflare_email_routing_rule.address | resource |
cloudflare_email_routing_settings.domain | resource |
aws_region.current | data source |
cloudflare_zone.domain | data source |
Name | Description | Type | Default | Required |
---|---|---|---|---|
extra_recipients | Extra recipients which will have SMTP accounts and configuration sets always created for them, even if there are no addresses redirecting to them. Use it to temporarily remove or change a redirect without forcing the config to remove the SMTP account for the recipient. | list(string) |
[] |
no |
mail_from_subdomain | MAIL FROM subdomain to configure in SES (instead of amazonses.com). Currently mandatory. | string |
"mail" |
no |
pgp_key | PGP key to encrypt the password with. If unset, plaintext password will be in the outputs. | string |
null |
no |
redirects | Mapping from domain name to a mapping of addresses in this domain to recipient addresses. | map(map(string)) |
n/a | yes |
wait_for_verification_timeout | Number of seconds to wait for recipient addresses to become verified in Cloudflare. If not null, the module will block creation of redirects and AWS SES entities until the recipient address is verified. This is useful to ensure that the initial SES verification email gets delivered using the new redirect. | number |
300 |
no |
Name | Description |
---|---|
smtp_accounts | The list of SMTP accounts that the module creates: one account per unique mail recipient. Mapping from recipient email address to their SMTP account credentials, which is an object containing username , password , and encrypted_password fields. password is not null only if var.pgp_key is null, and encrypted_password is not null only if var.pgp_key is not null. encrypted_password is a base64-encoded PGP-encrypted password, use base64 -d and pgp --decrypt to decrypt. |