-
Notifications
You must be signed in to change notification settings - Fork 9.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2029 from mintuhouse/f-ses-mail-from
Add support for SES MAIL FROM
- Loading branch information
Showing
5 changed files
with
355 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
package aws | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
|
||
"github.com/aws/aws-sdk-go/aws" | ||
"github.com/aws/aws-sdk-go/service/ses" | ||
"github.com/hashicorp/terraform/helper/schema" | ||
) | ||
|
||
func resourceAwsSesDomainMailFrom() *schema.Resource { | ||
return &schema.Resource{ | ||
Create: resourceAwsSesDomainMailFromSet, | ||
Read: resourceAwsSesDomainMailFromRead, | ||
Update: resourceAwsSesDomainMailFromSet, | ||
Delete: resourceAwsSesDomainMailFromDelete, | ||
Importer: &schema.ResourceImporter{ | ||
State: schema.ImportStatePassthrough, | ||
}, | ||
|
||
Schema: map[string]*schema.Schema{ | ||
"domain": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
}, | ||
"mail_from_domain": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
}, | ||
"behavior_on_mx_failure": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
Default: ses.BehaviorOnMXFailureUseDefaultValue, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func resourceAwsSesDomainMailFromSet(d *schema.ResourceData, meta interface{}) error { | ||
conn := meta.(*AWSClient).sesConn | ||
|
||
behaviorOnMxFailure := d.Get("behavior_on_mx_failure").(string) | ||
domainName := d.Get("domain").(string) | ||
mailFromDomain := d.Get("mail_from_domain").(string) | ||
|
||
input := &ses.SetIdentityMailFromDomainInput{ | ||
BehaviorOnMXFailure: aws.String(behaviorOnMxFailure), | ||
Identity: aws.String(domainName), | ||
MailFromDomain: aws.String(mailFromDomain), | ||
} | ||
|
||
_, err := conn.SetIdentityMailFromDomain(input) | ||
if err != nil { | ||
return fmt.Errorf("Error setting MAIL FROM domain: %s", err) | ||
} | ||
|
||
d.SetId(domainName) | ||
|
||
return resourceAwsSesDomainMailFromRead(d, meta) | ||
} | ||
|
||
func resourceAwsSesDomainMailFromRead(d *schema.ResourceData, meta interface{}) error { | ||
conn := meta.(*AWSClient).sesConn | ||
|
||
domainName := d.Id() | ||
|
||
readOpts := &ses.GetIdentityMailFromDomainAttributesInput{ | ||
Identities: []*string{ | ||
aws.String(domainName), | ||
}, | ||
} | ||
|
||
out, err := conn.GetIdentityMailFromDomainAttributes(readOpts) | ||
if err != nil { | ||
log.Printf("error fetching MAIL FROM domain attributes for %s: %s", domainName, err) | ||
return err | ||
} | ||
|
||
d.Set("domain", domainName) | ||
|
||
if v, ok := out.MailFromDomainAttributes[domainName]; ok { | ||
d.Set("behavior_on_mx_failure", v.BehaviorOnMXFailure) | ||
d.Set("mail_from_domain", v.MailFromDomain) | ||
} else { | ||
d.Set("behavior_on_mx_failure", v.BehaviorOnMXFailure) | ||
d.Set("mail_from_domain", "") | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func resourceAwsSesDomainMailFromDelete(d *schema.ResourceData, meta interface{}) error { | ||
conn := meta.(*AWSClient).sesConn | ||
|
||
domainName := d.Id() | ||
|
||
deleteOpts := &ses.SetIdentityMailFromDomainInput{ | ||
Identity: aws.String(domainName), | ||
MailFromDomain: nil, | ||
} | ||
|
||
_, err := conn.SetIdentityMailFromDomain(deleteOpts) | ||
if err != nil { | ||
return fmt.Errorf("Error deleting SES domain identity: %s", err) | ||
} | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,170 @@ | ||
package aws | ||
|
||
import ( | ||
"fmt" | ||
"testing" | ||
|
||
"github.com/aws/aws-sdk-go/aws" | ||
"github.com/aws/aws-sdk-go/service/ses" | ||
"github.com/hashicorp/terraform/helper/acctest" | ||
"github.com/hashicorp/terraform/helper/resource" | ||
"github.com/hashicorp/terraform/terraform" | ||
) | ||
|
||
func TestAccAwsSESDomainMailFrom_basic(t *testing.T) { | ||
domain := fmt.Sprintf( | ||
"%s.terraformtesting.com", | ||
acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum)) | ||
mailFromDomain1 := fmt.Sprintf("bounce1.%s", domain) | ||
mailFromDomain2 := fmt.Sprintf("bounce2.%s", domain) | ||
resourceName := "aws_ses_domain_mail_from.test" | ||
|
||
resource.Test(t, resource.TestCase{ | ||
PreCheck: func() { testAccPreCheck(t) }, | ||
Providers: testAccProviders, | ||
CheckDestroy: testAccCheckSESDomainMailFromDestroy, | ||
Steps: []resource.TestStep{ | ||
{ | ||
Config: testAccAwsSESDomainMailFromConfig(domain, mailFromDomain1), | ||
Check: resource.ComposeTestCheckFunc( | ||
testAccCheckAwsSESDomainMailFromExists(resourceName), | ||
resource.TestCheckResourceAttr(resourceName, "behavior_on_mx_failure", ses.BehaviorOnMXFailureUseDefaultValue), | ||
resource.TestCheckResourceAttr(resourceName, "domain", domain), | ||
resource.TestCheckResourceAttr(resourceName, "mail_from_domain", mailFromDomain1), | ||
), | ||
}, | ||
{ | ||
Config: testAccAwsSESDomainMailFromConfig(domain, mailFromDomain2), | ||
Check: resource.ComposeTestCheckFunc( | ||
testAccCheckAwsSESDomainMailFromExists(resourceName), | ||
resource.TestCheckResourceAttr(resourceName, "behavior_on_mx_failure", ses.BehaviorOnMXFailureUseDefaultValue), | ||
resource.TestCheckResourceAttr(resourceName, "domain", domain), | ||
resource.TestCheckResourceAttr(resourceName, "mail_from_domain", mailFromDomain2), | ||
), | ||
}, | ||
{ | ||
ResourceName: resourceName, | ||
ImportState: true, | ||
ImportStateVerify: true, | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func TestAccAwsSESDomainMailFrom_behaviorOnMxFailure(t *testing.T) { | ||
domain := fmt.Sprintf( | ||
"%s.terraformtesting.com", | ||
acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum)) | ||
resourceName := "aws_ses_domain_mail_from.test" | ||
|
||
resource.Test(t, resource.TestCase{ | ||
PreCheck: func() { testAccPreCheck(t) }, | ||
Providers: testAccProviders, | ||
CheckDestroy: testAccCheckSESDomainMailFromDestroy, | ||
Steps: []resource.TestStep{ | ||
{ | ||
Config: testAccAwsSESDomainMailFromConfig_behaviorOnMxFailure(domain, ses.BehaviorOnMXFailureUseDefaultValue), | ||
Check: resource.ComposeTestCheckFunc( | ||
testAccCheckAwsSESDomainMailFromExists(resourceName), | ||
resource.TestCheckResourceAttr(resourceName, "behavior_on_mx_failure", ses.BehaviorOnMXFailureUseDefaultValue), | ||
), | ||
}, | ||
{ | ||
Config: testAccAwsSESDomainMailFromConfig_behaviorOnMxFailure(domain, ses.BehaviorOnMXFailureRejectMessage), | ||
Check: resource.ComposeTestCheckFunc( | ||
testAccCheckAwsSESDomainMailFromExists(resourceName), | ||
resource.TestCheckResourceAttr(resourceName, "behavior_on_mx_failure", ses.BehaviorOnMXFailureRejectMessage), | ||
), | ||
}, | ||
{ | ||
ResourceName: resourceName, | ||
ImportState: true, | ||
ImportStateVerify: true, | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func testAccCheckAwsSESDomainMailFromExists(n string) resource.TestCheckFunc { | ||
return func(s *terraform.State) error { | ||
rs, ok := s.RootModule().Resources[n] | ||
if !ok { | ||
return fmt.Errorf("SES Domain Identity not found: %s", n) | ||
} | ||
|
||
if rs.Primary.ID == "" { | ||
return fmt.Errorf("SES Domain Identity name not set") | ||
} | ||
|
||
domain := rs.Primary.ID | ||
conn := testAccProvider.Meta().(*AWSClient).sesConn | ||
|
||
params := &ses.GetIdentityMailFromDomainAttributesInput{ | ||
Identities: []*string{ | ||
aws.String(domain), | ||
}, | ||
} | ||
|
||
response, err := conn.GetIdentityMailFromDomainAttributes(params) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if response.MailFromDomainAttributes[domain] == nil { | ||
return fmt.Errorf("SES Domain MAIL FROM %s not found in AWS", domain) | ||
} | ||
|
||
return nil | ||
} | ||
} | ||
|
||
func testAccCheckSESDomainMailFromDestroy(s *terraform.State) error { | ||
conn := testAccProvider.Meta().(*AWSClient).sesConn | ||
|
||
for _, rs := range s.RootModule().Resources { | ||
if rs.Type != "aws_ses_domain_mail_from" { | ||
continue | ||
} | ||
|
||
input := &ses.GetIdentityMailFromDomainAttributesInput{ | ||
Identities: []*string{aws.String(rs.Primary.ID)}, | ||
} | ||
|
||
out, err := conn.GetIdentityMailFromDomainAttributes(input) | ||
if err != nil { | ||
return fmt.Errorf("error fetching MAIL FROM domain attributes: %s", err) | ||
} | ||
if v, ok := out.MailFromDomainAttributes[rs.Primary.ID]; ok && v.MailFromDomain != nil && *v.MailFromDomain != "" { | ||
return fmt.Errorf("MAIL FROM domain was not removed, found: %s", *v.MailFromDomain) | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func testAccAwsSESDomainMailFromConfig(domain, mailFromDomain string) string { | ||
return fmt.Sprintf(` | ||
resource "aws_ses_domain_identity" "test" { | ||
domain = "%s" | ||
} | ||
resource "aws_ses_domain_mail_from" "test" { | ||
domain = "${aws_ses_domain_identity.test.domain}" | ||
mail_from_domain = "%s" | ||
} | ||
`, domain, mailFromDomain) | ||
} | ||
|
||
func testAccAwsSESDomainMailFromConfig_behaviorOnMxFailure(domain, behaviorOnMxFailure string) string { | ||
return fmt.Sprintf(` | ||
resource "aws_ses_domain_identity" "test" { | ||
domain = "%s" | ||
} | ||
resource "aws_ses_domain_mail_from" "test" { | ||
behavior_on_mx_failure = "%s" | ||
domain = "${aws_ses_domain_identity.test.domain}" | ||
mail_from_domain = "bounce.${aws_ses_domain_identity.test.domain}" | ||
} | ||
`, domain, behaviorOnMxFailure) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
--- | ||
layout: "aws" | ||
page_title: "AWS: ses_domain_mail_from" | ||
sidebar_current: "docs-aws-resource-ses-domain-mail-from" | ||
description: |- | ||
Provides an SES domain MAIL FROM resource | ||
--- | ||
|
||
# aws_ses_domain_mail_from | ||
|
||
Provides an SES domain MAIL FROM resource. | ||
|
||
~> **NOTE:** For the MAIL FROM domain to be fully usable, this resource should be paired with the [aws_ses_domain_identity resource](/docs/providers/aws/r/ses_domain_identity.html). To validate the MAIL FROM domain, a DNS MX record is required. To pass SPF checks, a DNS TXT record may also be required. See the [Amazon SES MAIL FROM documentation](https://docs.aws.amazon.com/ses/latest/DeveloperGuide/mail-from-set.html) for more information. | ||
|
||
## Example Usage | ||
|
||
```hcl | ||
resource "aws_ses_domain_mail_from" "example" { | ||
domain = "${aws_ses_domain_identity.example.domain}" | ||
mail_from_domain = "bounce.${aws_ses_domain_identity.example.domain}" | ||
} | ||
# Example SES Domain Identity | ||
resource "aws_ses_domain_identity" "example" { | ||
domain = "example.com" | ||
} | ||
# Example Route53 MX record | ||
resource "aws_route53_record" "example_ses_domain_mail_from_mx" { | ||
zone_id = "${aws_route53_zone.example.id}" | ||
name = "${aws_ses_domain_mail_from.example.mail_from_domain}" | ||
type = "MX" | ||
ttl = "600" | ||
records = ["10 feedback-smtp.us-east-1.amazonses.com"] # Change to the region in which `aws_ses_domain_identity.example` is created | ||
} | ||
# Example Route53 TXT record for SPF | ||
resource "aws_route53_record" "example_ses_domain_mail_from_txt" { | ||
zone_id = "${aws_route53_zone.example.id}" | ||
name = "${aws_ses_domain_mail_from.example.mail_from_domain}" | ||
type = "TXT" | ||
ttl = "600" | ||
records = ["v=spf1 include:amazonses.com -all"] | ||
} | ||
``` | ||
|
||
## Argument Reference | ||
|
||
The following arguments are required: | ||
|
||
* `domain` - (Required) Verified domain name to generate DKIM tokens for. | ||
* `mail_from_domain` - (Required) Subdomain (of above domain) which is to be used as MAIL FROM address (Required for DMARC validation) | ||
|
||
The following arguments are optional: | ||
|
||
* `behavior_on_mx_failure` - (Optional) The action that you want Amazon SES to take if it cannot successfully read the required MX record when you send an email. Defaults to `UseDefaultValue`. See the [SES API documentation](https://docs.aws.amazon.com/ses/latest/APIReference/API_SetIdentityMailFromDomain.html) for more information. | ||
|
||
## Attributes Reference | ||
|
||
In addition to the arguments, which are exported, the following attributes are exported: | ||
|
||
* `id` - The domain name. | ||
|
||
## Import | ||
|
||
MAIL FROM domain can be imported using the `domain` attribute, e.g. | ||
|
||
``` | ||
$ terraform import aws_ses_domain_mail_from.example example.com | ||
``` |