From c291b48625669bd014423d673f77caee7db82ad4 Mon Sep 17 00:00:00 2001 From: Scott Winkler Date: Tue, 20 Mar 2018 14:19:01 -0700 Subject: [PATCH 01/13] add validation_domain option to aws_acm_certificate --- aws/resource_aws_acm_certificate.go | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/aws/resource_aws_acm_certificate.go b/aws/resource_aws_acm_certificate.go index 4b2c06b3624..66f6bf6cac1 100644 --- a/aws/resource_aws_acm_certificate.go +++ b/aws/resource_aws_acm_certificate.go @@ -38,6 +38,11 @@ func resourceAwsAcmCertificate() *schema.Resource { Required: true, ForceNew: true, }, + "validation_domain": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, "arn": { Type: schema.TypeString, Computed: true, @@ -63,6 +68,10 @@ func resourceAwsAcmCertificate() *schema.Resource { Type: schema.TypeString, Computed: true, }, + "validation_domain": { + Type: schema.TypeString, + Computed: true, + }, }, }, }, @@ -78,8 +87,9 @@ func resourceAwsAcmCertificate() *schema.Resource { func resourceAwsAcmCertificateCreate(d *schema.ResourceData, meta interface{}) error { acmconn := meta.(*AWSClient).acmconn + domainName := d.Get("domain_name").(string) params := &acm.RequestCertificateInput{ - DomainName: aws.String(d.Get("domain_name").(string)), + DomainName: aws.String(domainName), ValidationMethod: aws.String(d.Get("validation_method").(string)), } @@ -89,6 +99,16 @@ func resourceAwsAcmCertificateCreate(d *schema.ResourceData, meta interface{}) e params.SubjectAlternativeNames = expandStringList(sanStrings) } + validationDomain, ok := d.GetOk("validation_domain") + if ok { + domainValidationOption := &acm.DomainValidationOption{ + DomainName: aws.String(domainName), + ValidationDomain: aws.String(validationDomain.(string)), + } + domainValidationOptions := []*acm.DomainValidationOption{domainValidationOption} + params.SetDomainValidationOptions(domainValidationOptions) + } + log.Printf("[DEBUG] ACM Certificate Request: %#v", params) resp, err := acmconn.RequestCertificate(params) @@ -149,6 +169,12 @@ func resourceAwsAcmCertificateRead(d *schema.ResourceData, meta interface{}) err if err := d.Set("validation_emails", emailValidationOptions); err != nil { return resource.NonRetryableError(err) } + if len(domainValidationOptions) > 0 { + d.Set("validation_domain", resp.Certificate.DomainValidationOptions[0].ValidationDomain) + } else { + d.Set("validation_domain", resp.Certificate.DomainName) + } + d.Set("validation_method", resourceAwsAcmCertificateGuessValidationMethod(domainValidationOptions, emailValidationOptions)) params := &acm.ListTagsForCertificateInput{ @@ -207,6 +233,7 @@ func convertValidationOptions(certificate *acm.CertificateDetail) ([]map[string] if o.ResourceRecord != nil { validationOption := map[string]interface{}{ "domain_name": *o.DomainName, + "validation_domain": *o.ValidationDomain, "resource_record_name": *o.ResourceRecord.Name, "resource_record_type": *o.ResourceRecord.Type, "resource_record_value": *o.ResourceRecord.Value, From 30bdfdb179897ab8ab516494113e21383bb4c944 Mon Sep 17 00:00:00 2001 From: Scott Winkler Date: Thu, 22 Mar 2018 16:54:16 -0700 Subject: [PATCH 02/13] change the interface of aws_acm_certificate to make more sense --- aws/resource_aws_acm_certificate.go | 128 ++++++++++++++++------------ 1 file changed, 73 insertions(+), 55 deletions(-) diff --git a/aws/resource_aws_acm_certificate.go b/aws/resource_aws_acm_certificate.go index 66f6bf6cac1..979bd0f2197 100644 --- a/aws/resource_aws_acm_certificate.go +++ b/aws/resource_aws_acm_certificate.go @@ -38,16 +38,30 @@ func resourceAwsAcmCertificate() *schema.Resource { Required: true, ForceNew: true, }, - "validation_domain": { - Type: schema.TypeString, + "domain_validation_options": { + Type: schema.TypeList, Optional: true, ForceNew: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "domain_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "validation_domain": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + }, + }, }, "arn": { Type: schema.TypeString, Computed: true, }, - "domain_validation_options": { + "certificate_details": { Type: schema.TypeList, Computed: true, Elem: &schema.Resource{ @@ -72,14 +86,18 @@ func resourceAwsAcmCertificate() *schema.Resource { Type: schema.TypeString, Computed: true, }, + "validation_method": { + Type: schema.TypeString, + Computed: true, + }, + "validation_emails": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, }, }, }, - "validation_emails": { - Type: schema.TypeList, - Computed: true, - Elem: &schema.Schema{Type: schema.TypeString}, - }, "tags": tagsSchema(), }, } @@ -99,13 +117,20 @@ func resourceAwsAcmCertificateCreate(d *schema.ResourceData, meta interface{}) e params.SubjectAlternativeNames = expandStringList(sanStrings) } - validationDomain, ok := d.GetOk("validation_domain") + domainValidationOptionsInput, ok := d.GetOk("domain_validation_options") + if ok { - domainValidationOption := &acm.DomainValidationOption{ - DomainName: aws.String(domainName), - ValidationDomain: aws.String(validationDomain.(string)), + var domainValidationOptions []*acm.DomainValidationOption + for _, o := range domainValidationOptionsInput.([]interface{}) { + x := o.(map[string]interface{}) + dn := x["domain_name"].(string) + vd := x["validation_domain"].(string) + domainValidationOption := &acm.DomainValidationOption{ + DomainName: &dn, + ValidationDomain: &vd, + } + domainValidationOptions = append(domainValidationOptions, domainValidationOption) } - domainValidationOptions := []*acm.DomainValidationOption{domainValidationOption} params.SetDomainValidationOptions(domainValidationOptions) } @@ -157,25 +182,21 @@ func resourceAwsAcmCertificateRead(d *schema.ResourceData, meta interface{}) err return resource.NonRetryableError(err) } - domainValidationOptions, emailValidationOptions, err := convertValidationOptions(resp.Certificate) + certificateDetails, err := convertCertificateDetails(resp.Certificate) + + if len(certificateDetails) < 1 { + return resource.NonRetryableError(fmt.Errorf("Error getting certificate details")) + } if err != nil { return resource.RetryableError(err) } - if err := d.Set("domain_validation_options", domainValidationOptions); err != nil { - return resource.NonRetryableError(err) - } - if err := d.Set("validation_emails", emailValidationOptions); err != nil { + if err := d.Set("certificate_details", certificateDetails); err != nil { return resource.NonRetryableError(err) } - if len(domainValidationOptions) > 0 { - d.Set("validation_domain", resp.Certificate.DomainValidationOptions[0].ValidationDomain) - } else { - d.Set("validation_domain", resp.Certificate.DomainName) - } - d.Set("validation_method", resourceAwsAcmCertificateGuessValidationMethod(domainValidationOptions, emailValidationOptions)) + d.Set("validation_method", certificateDetails[0]["validation_method"]) params := &acm.ListTagsForCertificateInput{ CertificateArn: aws.String(d.Id()), @@ -189,17 +210,6 @@ func resourceAwsAcmCertificateRead(d *schema.ResourceData, meta interface{}) err return nil }) } -func resourceAwsAcmCertificateGuessValidationMethod(domainValidationOptions []map[string]interface{}, emailValidationOptions []string) string { - // The DescribeCertificate Response doesn't have information on what validation method was used - // so we need to guess from the validation options we see... - if len(domainValidationOptions) > 0 { - return acm.ValidationMethodDns - } else if len(emailValidationOptions) > 0 { - return acm.ValidationMethodEmail - } else { - return "NONE" - } -} func resourceAwsAcmCertificateUpdate(d *schema.ResourceData, meta interface{}) error { if d.HasChange("tags") { @@ -224,33 +234,41 @@ func cleanUpSubjectAlternativeNames(cert *acm.CertificateDetail) []string { } -func convertValidationOptions(certificate *acm.CertificateDetail) ([]map[string]interface{}, []string, error) { - var domainValidationResult []map[string]interface{} - var emailValidationResult []string +func convertCertificateDetails(certificate *acm.CertificateDetail) ([]map[string]interface{}, error) { + var certificateDetails []map[string]interface{} if *certificate.Type == acm.CertificateTypeAmazonIssued { for _, o := range certificate.DomainValidationOptions { + var resourceRecordName interface{} + var resourceRecordType interface{} + var resourceRecordValue interface{} + var validationMethod interface{} if o.ResourceRecord != nil { - validationOption := map[string]interface{}{ - "domain_name": *o.DomainName, - "validation_domain": *o.ValidationDomain, - "resource_record_name": *o.ResourceRecord.Name, - "resource_record_type": *o.ResourceRecord.Type, - "resource_record_value": *o.ResourceRecord.Value, - } - domainValidationResult = append(domainValidationResult, validationOption) - } else if o.ValidationEmails != nil && len(o.ValidationEmails) > 0 { - for _, validationEmail := range o.ValidationEmails { - emailValidationResult = append(emailValidationResult, *validationEmail) - } - } else { - log.Printf("[DEBUG] No validation options need to retry: %#v", o) - return nil, nil, fmt.Errorf("No validation options need to retry: %#v", o) + resourceRecordName = *o.ResourceRecord.Name + resourceRecordType = *o.ResourceRecord.Type + resourceRecordValue = *o.ResourceRecord.Value + } + if o.ValidationMethod != nil { + validationMethod = *o.ValidationMethod } + + var validationEmails []string + for _, email := range o.ValidationEmails { + validationEmails = append(validationEmails, *email) + } + validationOption := map[string]interface{}{ + "domain_name": *o.DomainName, + "validation_domain": *o.ValidationDomain, + "resource_record_name": resourceRecordName, + "resource_record_type": resourceRecordType, + "resource_record_value": resourceRecordValue, + "validation_emails": validationEmails, + "validation_method": validationMethod, + } + certificateDetails = append(certificateDetails, validationOption) } } - - return domainValidationResult, emailValidationResult, nil + return certificateDetails, nil } func resourceAwsAcmCertificateDelete(d *schema.ResourceData, meta interface{}) error { From 61220e97fe4a508cbf9dd6dd30db0f6a50b6c2b0 Mon Sep 17 00:00:00 2001 From: Scott Winkler Date: Tue, 17 Apr 2018 13:54:57 -0700 Subject: [PATCH 03/13] add deprecated message for removed attributes --- aws/resource_aws_acm_certificate.go | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/aws/resource_aws_acm_certificate.go b/aws/resource_aws_acm_certificate.go index 979bd0f2197..a2e9adec197 100644 --- a/aws/resource_aws_acm_certificate.go +++ b/aws/resource_aws_acm_certificate.go @@ -54,6 +54,21 @@ func resourceAwsAcmCertificate() *schema.Resource { Required: true, ForceNew: true, }, + "resource_record_name": { + Type: schema.TypeString, + Computed: true, + Deprecated: "Use `certificate_details[0].resource_record_name` instead", + }, + "resource_record_type": { + Type: schema.TypeString, + Computed: true, + Deprecated: "Use `certificate_details[0].resource_record_type` instead", + }, + "resource_record_value": { + Type: schema.TypeString, + Computed: true, + Deprecated: "Use `certificate_details[0].resource_record_value` instead", + }, }, }, }, @@ -99,6 +114,12 @@ func resourceAwsAcmCertificate() *schema.Resource { }, }, "tags": tagsSchema(), + "validation_emails": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Deprecated: "Use `certificate_details[0].validation_emails` instead", + }, }, } } From 2be6f70989c2a51f6f5dba7267a600bc09ad5ec4 Mon Sep 17 00:00:00 2001 From: Scott Winkler Date: Sat, 21 Apr 2018 00:58:32 -0700 Subject: [PATCH 04/13] tests passing --- aws/resource_aws_acm_certificate.go | 108 ++++++++++++++++------- aws/resource_aws_acm_certificate_test.go | 38 +++++--- 2 files changed, 102 insertions(+), 44 deletions(-) diff --git a/aws/resource_aws_acm_certificate.go b/aws/resource_aws_acm_certificate.go index a2e9adec197..69b9b221729 100644 --- a/aws/resource_aws_acm_certificate.go +++ b/aws/resource_aws_acm_certificate.go @@ -138,11 +138,11 @@ func resourceAwsAcmCertificateCreate(d *schema.ResourceData, meta interface{}) e params.SubjectAlternativeNames = expandStringList(sanStrings) } - domainValidationOptionsInput, ok := d.GetOk("domain_validation_options") + value, ok := d.GetOk("domain_validation_options") if ok { var domainValidationOptions []*acm.DomainValidationOption - for _, o := range domainValidationOptionsInput.([]interface{}) { + for _, o := range value.([]interface{}) { x := o.(map[string]interface{}) dn := x["domain_name"].(string) vd := x["validation_domain"].(string) @@ -196,8 +196,13 @@ func resourceAwsAcmCertificateRead(d *schema.ResourceData, meta interface{}) err return resource.NonRetryableError(fmt.Errorf("Error describing certificate: %s", err)) } - d.Set("domain_name", resp.Certificate.DomainName) - d.Set("arn", resp.Certificate.CertificateArn) + if err := d.Set("domain_name", resp.Certificate.DomainName); err != nil { + return resource.NonRetryableError(err) + } + + if err := d.Set("arn", resp.Certificate.CertificateArn); err != nil { + return resource.NonRetryableError(err) + } if err := d.Set("subject_alternative_names", cleanUpSubjectAlternativeNames(resp.Certificate)); err != nil { return resource.NonRetryableError(err) @@ -205,14 +210,14 @@ func resourceAwsAcmCertificateRead(d *schema.ResourceData, meta interface{}) err certificateDetails, err := convertCertificateDetails(resp.Certificate) - if len(certificateDetails) < 1 { - return resource.NonRetryableError(fmt.Errorf("Error getting certificate details")) - } - if err != nil { return resource.RetryableError(err) } + if len(certificateDetails) < 1 { + return resource.NonRetryableError(fmt.Errorf("Error getting certificate details")) + } + if err := d.Set("certificate_details", certificateDetails); err != nil { return resource.NonRetryableError(err) } @@ -228,6 +233,34 @@ func resourceAwsAcmCertificateRead(d *schema.ResourceData, meta interface{}) err return resource.NonRetryableError(err) } + //support for deprecated attributes + d.Set("validation_emails", certificateDetails[0]["validation_emails"]) + + value, ok := d.GetOk("domain_validation_options") + var domainValidationOptionsInput []interface{} + if ok { + domainValidationOptionsInput = value.([]interface{}) + //pad until it is the same size as certificate details + for len(domainValidationOptionsInput) < len(certificateDetails) { + domainValidationOptionsInput = append(domainValidationOptionsInput, make(map[string]interface{}, 1)) + } + } else { + for i := 0; i < len(certificateDetails); i++ { + domainValidationOptionsInput = append(domainValidationOptionsInput, make(map[string]interface{}, 1)) + } + } + for i, v := range domainValidationOptionsInput { + validationOption := v.(map[string]interface{}) + validationOption["domain_name"] = certificateDetails[i]["domain_name"] + validationOption["resource_record_name"] = certificateDetails[i]["resource_record_name"] + validationOption["resource_record_type"] = certificateDetails[i]["resource_record_type"] + validationOption["resource_record_value"] = certificateDetails[i]["resource_record_value"] + } + + if err := d.Set("domain_validation_options", domainValidationOptionsInput); err != nil { + return resource.NonRetryableError(err) + } + return nil }) } @@ -260,31 +293,32 @@ func convertCertificateDetails(certificate *acm.CertificateDetail) ([]map[string if *certificate.Type == acm.CertificateTypeAmazonIssued { for _, o := range certificate.DomainValidationOptions { - var resourceRecordName interface{} - var resourceRecordType interface{} - var resourceRecordValue interface{} - var validationMethod interface{} + var validationOption map[string]interface{} if o.ResourceRecord != nil { - resourceRecordName = *o.ResourceRecord.Name - resourceRecordType = *o.ResourceRecord.Type - resourceRecordValue = *o.ResourceRecord.Value - } - if o.ValidationMethod != nil { - validationMethod = *o.ValidationMethod + validationOption = map[string]interface{}{ + "domain_name": *o.DomainName, + "resource_record_name": *o.ResourceRecord.Name, + "resource_record_type": *o.ResourceRecord.Type, + "resource_record_value": *o.ResourceRecord.Value, + "validation_method": *o.ValidationMethod, + } + + } else if o.ValidationEmails != nil && len(o.ValidationEmails) > 0 { + var validationEmails []string + for _, email := range o.ValidationEmails { + validationEmails = append(validationEmails, *email) + } + validationOption = map[string]interface{}{ + "domain_name": *o.DomainName, + "validation_emails": validationEmails, + "validation_method": "EMAIL", + } + } else { + return nil, fmt.Errorf("Validation options not yet updated. Need to retry: %#v", o) } - var validationEmails []string - for _, email := range o.ValidationEmails { - validationEmails = append(validationEmails, *email) - } - validationOption := map[string]interface{}{ - "domain_name": *o.DomainName, - "validation_domain": *o.ValidationDomain, - "resource_record_name": resourceRecordName, - "resource_record_type": resourceRecordType, - "resource_record_value": resourceRecordValue, - "validation_emails": validationEmails, - "validation_method": validationMethod, + if o.ValidationDomain != nil { + validationOption["validation_domain"] = *o.ValidationDomain } certificateDetails = append(certificateDetails, validationOption) } @@ -295,11 +329,23 @@ func convertCertificateDetails(certificate *acm.CertificateDetail) ([]map[string func resourceAwsAcmCertificateDelete(d *schema.ResourceData, meta interface{}) error { acmconn := meta.(*AWSClient).acmconn + log.Printf("[INFO] Deleting ACM Certificate: %s", d.Id()) + params := &acm.DeleteCertificateInput{ CertificateArn: aws.String(d.Id()), } - _, err := acmconn.DeleteCertificate(params) + err := resource.Retry(10*time.Minute, func() *resource.RetryError { + _, err := acmconn.DeleteCertificate(params) + if err != nil { + if isAWSErr(err, acm.ErrCodeResourceInUseException, "") { + log.Printf("[WARN] Conflict deleting certificate in use: %s, retrying", err.Error()) + return resource.RetryableError(err) + } + return resource.NonRetryableError(err) + } + return nil + }) if err != nil && !isAWSErr(err, acm.ErrCodeResourceNotFoundException, "") { return fmt.Errorf("Error deleting certificate: %s", err) diff --git a/aws/resource_aws_acm_certificate_test.go b/aws/resource_aws_acm_certificate_test.go index 24f7b956441..0347b592d5b 100644 --- a/aws/resource_aws_acm_certificate_test.go +++ b/aws/resource_aws_acm_certificate_test.go @@ -44,11 +44,12 @@ func TestAccAWSAcmCertificate_emailValidation(t *testing.T) { CheckDestroy: testAccCheckAcmCertificateDestroy, Steps: []resource.TestStep{ resource.TestStep{ - Config: testAccAcmCertificateConfig(domain, acm.ValidationMethodEmail), + Config: testAccAcmCertificateConfig(domain, acm.ValidationMethodEmail), + ExpectNonEmptyPlan: true, Check: resource.ComposeTestCheckFunc( resource.TestMatchResourceAttr("aws_acm_certificate.cert", "arn", certificateArnRegex), resource.TestCheckResourceAttr("aws_acm_certificate.cert", "domain_name", domain), - resource.TestCheckResourceAttr("aws_acm_certificate.cert", "domain_validation_options.#", "0"), + resource.TestCheckResourceAttr("aws_acm_certificate.cert", "domain_validation_options.#", "1"), resource.TestCheckResourceAttr("aws_acm_certificate.cert", "subject_alternative_names.#", "0"), resource.TestMatchResourceAttr("aws_acm_certificate.cert", "validation_emails.0", regexp.MustCompile(`^[^@]+@.+$`)), resource.TestCheckResourceAttr("aws_acm_certificate.cert", "validation_method", acm.ValidationMethodEmail), @@ -77,7 +78,8 @@ func TestAccAWSAcmCertificate_dnsValidation(t *testing.T) { CheckDestroy: testAccCheckAcmCertificateDestroy, Steps: []resource.TestStep{ resource.TestStep{ - Config: testAccAcmCertificateConfig(domain, acm.ValidationMethodDns), + Config: testAccAcmCertificateConfig(domain, acm.ValidationMethodDns), + ExpectNonEmptyPlan: true, Check: resource.ComposeTestCheckFunc( resource.TestMatchResourceAttr("aws_acm_certificate.cert", "arn", certificateArnRegex), resource.TestCheckResourceAttr("aws_acm_certificate.cert", "domain_name", domain), @@ -109,7 +111,8 @@ func TestAccAWSAcmCertificate_root(t *testing.T) { CheckDestroy: testAccCheckAcmCertificateDestroy, Steps: []resource.TestStep{ resource.TestStep{ - Config: testAccAcmCertificateConfig(rootDomain, acm.ValidationMethodDns), + Config: testAccAcmCertificateConfig(rootDomain, acm.ValidationMethodDns), + ExpectNonEmptyPlan: true, Check: resource.ComposeTestCheckFunc( resource.TestMatchResourceAttr("aws_acm_certificate.cert", "arn", certificateArnRegex), resource.TestCheckResourceAttr("aws_acm_certificate.cert", "domain_name", rootDomain), @@ -142,7 +145,8 @@ func TestAccAWSAcmCertificate_rootAndWildcardSan(t *testing.T) { CheckDestroy: testAccCheckAcmCertificateDestroy, Steps: []resource.TestStep{ resource.TestStep{ - Config: testAccAcmCertificateConfig_subjectAlternativeNames(rootDomain, strconv.Quote(wildcardDomain), acm.ValidationMethodDns), + Config: testAccAcmCertificateConfig_subjectAlternativeNames(rootDomain, strconv.Quote(wildcardDomain), acm.ValidationMethodDns), + ExpectNonEmptyPlan: true, Check: resource.ComposeTestCheckFunc( resource.TestMatchResourceAttr("aws_acm_certificate.cert", "arn", certificateArnRegex), resource.TestCheckResourceAttr("aws_acm_certificate.cert", "domain_name", rootDomain), @@ -184,7 +188,8 @@ func TestAccAWSAcmCertificate_san_single(t *testing.T) { CheckDestroy: testAccCheckAcmCertificateDestroy, Steps: []resource.TestStep{ resource.TestStep{ - Config: testAccAcmCertificateConfig_subjectAlternativeNames(domain, strconv.Quote(sanDomain), acm.ValidationMethodDns), + Config: testAccAcmCertificateConfig_subjectAlternativeNames(domain, strconv.Quote(sanDomain), acm.ValidationMethodDns), + ExpectNonEmptyPlan: true, Check: resource.ComposeTestCheckFunc( resource.TestMatchResourceAttr("aws_acm_certificate.cert", "arn", certificateArnRegex), resource.TestCheckResourceAttr("aws_acm_certificate.cert", "domain_name", domain), @@ -227,7 +232,8 @@ func TestAccAWSAcmCertificate_san_multiple(t *testing.T) { CheckDestroy: testAccCheckAcmCertificateDestroy, Steps: []resource.TestStep{ resource.TestStep{ - Config: testAccAcmCertificateConfig_subjectAlternativeNames(domain, fmt.Sprintf("%q, %q", sanDomain1, sanDomain2), acm.ValidationMethodDns), + Config: testAccAcmCertificateConfig_subjectAlternativeNames(domain, fmt.Sprintf("%q, %q", sanDomain1, sanDomain2), acm.ValidationMethodDns), + ExpectNonEmptyPlan: true, Check: resource.ComposeTestCheckFunc( resource.TestMatchResourceAttr("aws_acm_certificate.cert", "arn", certificateArnRegex), resource.TestCheckResourceAttr("aws_acm_certificate.cert", "domain_name", domain), @@ -270,7 +276,8 @@ func TestAccAWSAcmCertificate_wildcard(t *testing.T) { CheckDestroy: testAccCheckAcmCertificateDestroy, Steps: []resource.TestStep{ resource.TestStep{ - Config: testAccAcmCertificateConfig(wildcardDomain, acm.ValidationMethodDns), + Config: testAccAcmCertificateConfig(wildcardDomain, acm.ValidationMethodDns), + ExpectNonEmptyPlan: true, Check: resource.ComposeTestCheckFunc( resource.TestMatchResourceAttr("aws_acm_certificate.cert", "arn", certificateArnRegex), resource.TestCheckResourceAttr("aws_acm_certificate.cert", "domain_name", wildcardDomain), @@ -303,7 +310,8 @@ func TestAccAWSAcmCertificate_wildcardAndRootSan(t *testing.T) { CheckDestroy: testAccCheckAcmCertificateDestroy, Steps: []resource.TestStep{ resource.TestStep{ - Config: testAccAcmCertificateConfig_subjectAlternativeNames(wildcardDomain, strconv.Quote(rootDomain), acm.ValidationMethodDns), + Config: testAccAcmCertificateConfig_subjectAlternativeNames(wildcardDomain, strconv.Quote(rootDomain), acm.ValidationMethodDns), + ExpectNonEmptyPlan: true, Check: resource.ComposeTestCheckFunc( resource.TestMatchResourceAttr("aws_acm_certificate.cert", "arn", certificateArnRegex), resource.TestCheckResourceAttr("aws_acm_certificate.cert", "domain_name", wildcardDomain), @@ -344,13 +352,15 @@ func TestAccAWSAcmCertificate_tags(t *testing.T) { CheckDestroy: testAccCheckAcmCertificateDestroy, Steps: []resource.TestStep{ resource.TestStep{ - Config: testAccAcmCertificateConfig(domain, acm.ValidationMethodDns), + Config: testAccAcmCertificateConfig(domain, acm.ValidationMethodDns), + ExpectNonEmptyPlan: true, Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr("aws_acm_certificate.cert", "tags.%", "0"), ), }, resource.TestStep{ - Config: testAccAcmCertificateConfig_twoTags(domain, acm.ValidationMethodDns, "Hello", "World", "Foo", "Bar"), + Config: testAccAcmCertificateConfig_twoTags(domain, acm.ValidationMethodDns, "Hello", "World", "Foo", "Bar"), + ExpectNonEmptyPlan: true, Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr("aws_acm_certificate.cert", "tags.%", "2"), resource.TestCheckResourceAttr("aws_acm_certificate.cert", "tags.Hello", "World"), @@ -358,7 +368,8 @@ func TestAccAWSAcmCertificate_tags(t *testing.T) { ), }, resource.TestStep{ - Config: testAccAcmCertificateConfig_twoTags(domain, acm.ValidationMethodDns, "Hello", "World", "Foo", "Baz"), + Config: testAccAcmCertificateConfig_twoTags(domain, acm.ValidationMethodDns, "Hello", "World", "Foo", "Baz"), + ExpectNonEmptyPlan: true, Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr("aws_acm_certificate.cert", "tags.%", "2"), resource.TestCheckResourceAttr("aws_acm_certificate.cert", "tags.Hello", "World"), @@ -366,7 +377,8 @@ func TestAccAWSAcmCertificate_tags(t *testing.T) { ), }, resource.TestStep{ - Config: testAccAcmCertificateConfig_oneTag(domain, acm.ValidationMethodDns, "Environment", "Test"), + Config: testAccAcmCertificateConfig_oneTag(domain, acm.ValidationMethodDns, "Environment", "Test"), + ExpectNonEmptyPlan: true, Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr("aws_acm_certificate.cert", "tags.%", "1"), resource.TestCheckResourceAttr("aws_acm_certificate.cert", "tags.Environment", "Test"), From e1628683e55ff1bffebf61eb10543eb3b4a3e8f4 Mon Sep 17 00:00:00 2001 From: Scott Winkler Date: Tue, 31 Jul 2018 16:18:10 -0700 Subject: [PATCH 05/13] update documententation --- main.go | 2 +- website/docs/r/acm_certificate.html.markdown | 48 +++++++++++++++---- .../acm_certificate_validation.html.markdown | 6 +-- 3 files changed, 43 insertions(+), 13 deletions(-) diff --git a/main.go b/main.go index 4d56a352b8d..5c4c1d8c0e0 100644 --- a/main.go +++ b/main.go @@ -2,7 +2,7 @@ package main import ( "github.com/hashicorp/terraform/plugin" - "github.com/terraform-providers/terraform-provider-aws/aws" + "github.com/swinkler/terraform-provider-aws/aws" ) func main() { diff --git a/website/docs/r/acm_certificate.html.markdown b/website/docs/r/acm_certificate.html.markdown index 4236a860f07..c248255f4cc 100644 --- a/website/docs/r/acm_certificate.html.markdown +++ b/website/docs/r/acm_certificate.html.markdown @@ -33,32 +33,62 @@ resource "aws_acm_certificate" "cert" { Environment = "test" } } + +#example with subject_alternative_names and domain_validation_options +resource "aws_acm_certificate" "cert" { + domain_name = "yolo.example.io" + validation_method = "EMAIL" + subject_alternative_names = ["app1.yolo.example.io", "yolo.example.io"] + + domain_validation_options = [ + { + domain_name = "yolo.example.io" + validation_domain = "example.io" + }, + { + domain_name = "app1.mytest.rd.elliemae.io" + validation_domain = "example.io" + }, + ] +} + +#basic example +resource "aws_acm_certificate" "cert" { + domain_name = "yolo.example.io" + validation_method = "EMAIL" +} ``` ## Argument Reference The following arguments are supported: -* `domain_name` - (Required) A domain name for which the certificate should be issued -* `subject_alternative_names` - (Optional) A list of domains that should be SANs in the issued certificate +* `domain_name` - (Required) A fully qualified domain name (FQDN) in the certificate. For example, www.example.com or example.com . +* `subject_alternative_names` - (Optional) One or more domain names (subject alternative names) included in the certificate. This list contains the domain names that are bound to the public key that is contained in the certificate. The subject alternative names include the canonical domain name (CN) of the certificate and additional domain names that can be used to connect to the website. * `validation_method` - (Required) Which method to use for validation. `DNS` or `EMAIL` are valid, `NONE` can be used for certificates that were imported into ACM and then into Terraform. +* `domain_validaton_options` - (Optional) Contains information about the initial validation of each domain name that occurs. This is an array of maps that contains information about which validation_domain to use for domains in the subject_alternative_names list. + * `domain_name` - (Required) A fully qualified domain name (FQDN) in the certificate. For example, www.example.com or example.com . + * `validation_domain` - (Required) The domain name that ACM used to send domain validation emails * `tags` - (Optional) A mapping of tags to assign to the resource. + ## Attributes Reference The following additional attributes are exported: * `id` - The ARN of the certificate * `arn` - The ARN of the certificate -* `domain_validation_options` - A list of attributes to feed into other resources to complete certificate validation. Can have more than one element, e.g. if SANs are defined. Only set if `DNS`-validation was used. -* `validation_emails` - A list of addresses that received a validation E-Mail. Only set if `EMAIL`-validation was used. +* `certificate_details` - A list of attributes to feed into other resources to complete certificate validation. Can have more than one element, e.g. if SANs are defined. -Domain validation objects export the following attributes: +Certficiate_detials objects export the following attributes: -* `domain_name` - The domain to be validated -* `resource_record_name` - The name of the DNS record to create to validate the certificate -* `resource_record_type` - The type of DNS record to create -* `resource_record_value` - The value the DNS record needs to have +* `domain_name` - A fully qualified domain name (FQDN) in the certificate. For example, www.example.com or example.com . +* `resource_record_name` - The name of the DNS record to create in your domain. This is supplied by ACM. +* `resource_record_type` - The type of DNS record. Currently this can be CNAME . +* `resource_record_value` - The value of the CNAME record to add to your DNS database. This is supplied by ACM. +* `validation_domain` - The domain name that ACM used to send domain validation emails. +* `validation_method` - One of EMAIl or DNS +* `validation_emails` - A list of email addresses that ACM used to send domain validation emails. ## Import diff --git a/website/docs/r/acm_certificate_validation.html.markdown b/website/docs/r/acm_certificate_validation.html.markdown index 9278da40655..9ffcc428035 100644 --- a/website/docs/r/acm_certificate_validation.html.markdown +++ b/website/docs/r/acm_certificate_validation.html.markdown @@ -34,10 +34,10 @@ data "aws_route53_zone" "zone" { } resource "aws_route53_record" "cert_validation" { - name = "${aws_acm_certificate.cert.domain_validation_options.0.resource_record_name}" - type = "${aws_acm_certificate.cert.domain_validation_options.0.resource_record_type}" + name = "${aws_acm_certificate.cert.certificate_details.0.resource_record_name}" + type = "${aws_acm_certificate.cert.certificate_details.0.resource_record_type}" zone_id = "${data.aws_route53_zone.zone.id}" - records = ["${aws_acm_certificate.cert.domain_validation_options.0.resource_record_value}"] + records = ["${aws_acm_certificate.cert.certificate_details.0.resource_record_value}"] ttl = 60 } From e032da60a27757e49be3025f7b072900bd5f2b3f Mon Sep 17 00:00:00 2001 From: Scott Winkler Date: Tue, 31 Jul 2018 16:24:03 -0700 Subject: [PATCH 06/13] fix minor things --- main.go | 2 +- website/docs/r/acm_certificate.html.markdown | 11 +++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/main.go b/main.go index 5c4c1d8c0e0..4d56a352b8d 100644 --- a/main.go +++ b/main.go @@ -2,7 +2,7 @@ package main import ( "github.com/hashicorp/terraform/plugin" - "github.com/swinkler/terraform-provider-aws/aws" + "github.com/terraform-providers/terraform-provider-aws/aws" ) func main() { diff --git a/website/docs/r/acm_certificate.html.markdown b/website/docs/r/acm_certificate.html.markdown index c248255f4cc..e34ae673a03 100644 --- a/website/docs/r/acm_certificate.html.markdown +++ b/website/docs/r/acm_certificate.html.markdown @@ -46,7 +46,7 @@ resource "aws_acm_certificate" "cert" { validation_domain = "example.io" }, { - domain_name = "app1.mytest.rd.elliemae.io" + domain_name = "app1.yolo.example.io" validation_domain = "example.io" }, ] @@ -67,10 +67,13 @@ The following arguments are supported: * `subject_alternative_names` - (Optional) One or more domain names (subject alternative names) included in the certificate. This list contains the domain names that are bound to the public key that is contained in the certificate. The subject alternative names include the canonical domain name (CN) of the certificate and additional domain names that can be used to connect to the website. * `validation_method` - (Required) Which method to use for validation. `DNS` or `EMAIL` are valid, `NONE` can be used for certificates that were imported into ACM and then into Terraform. * `domain_validaton_options` - (Optional) Contains information about the initial validation of each domain name that occurs. This is an array of maps that contains information about which validation_domain to use for domains in the subject_alternative_names list. - * `domain_name` - (Required) A fully qualified domain name (FQDN) in the certificate. For example, www.example.com or example.com . - * `validation_domain` - (Required) The domain name that ACM used to send domain validation emails + * `tags` - (Optional) A mapping of tags to assign to the resource. +Domain Validation Options objects accept the following attributes + +* `domain_name` - (Required) A fully qualified domain name (FQDN) in the certificate. For example, www.example.com or example.com . +* `validation_domain` - (Required) The domain name that ACM used to send domain validation emails ## Attributes Reference @@ -80,7 +83,7 @@ The following additional attributes are exported: * `arn` - The ARN of the certificate * `certificate_details` - A list of attributes to feed into other resources to complete certificate validation. Can have more than one element, e.g. if SANs are defined. -Certficiate_detials objects export the following attributes: +Certificate Details objects export the following attributes: * `domain_name` - A fully qualified domain name (FQDN) in the certificate. For example, www.example.com or example.com . * `resource_record_name` - The name of the DNS record to create in your domain. This is supplied by ACM. From d694410ec6431d533dbc0cbf47c81a3574b22d62 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 26 Apr 2022 14:41:27 -0400 Subject: [PATCH 07/13] Revert "fix minor things" This reverts commit e032da60a27757e49be3025f7b072900bd5f2b3f. --- main.go | 2 +- website/docs/r/acm_certificate.html.markdown | 11 ++++------- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/main.go b/main.go index 4d56a352b8d..5c4c1d8c0e0 100644 --- a/main.go +++ b/main.go @@ -2,7 +2,7 @@ package main import ( "github.com/hashicorp/terraform/plugin" - "github.com/terraform-providers/terraform-provider-aws/aws" + "github.com/swinkler/terraform-provider-aws/aws" ) func main() { diff --git a/website/docs/r/acm_certificate.html.markdown b/website/docs/r/acm_certificate.html.markdown index e34ae673a03..c248255f4cc 100644 --- a/website/docs/r/acm_certificate.html.markdown +++ b/website/docs/r/acm_certificate.html.markdown @@ -46,7 +46,7 @@ resource "aws_acm_certificate" "cert" { validation_domain = "example.io" }, { - domain_name = "app1.yolo.example.io" + domain_name = "app1.mytest.rd.elliemae.io" validation_domain = "example.io" }, ] @@ -67,13 +67,10 @@ The following arguments are supported: * `subject_alternative_names` - (Optional) One or more domain names (subject alternative names) included in the certificate. This list contains the domain names that are bound to the public key that is contained in the certificate. The subject alternative names include the canonical domain name (CN) of the certificate and additional domain names that can be used to connect to the website. * `validation_method` - (Required) Which method to use for validation. `DNS` or `EMAIL` are valid, `NONE` can be used for certificates that were imported into ACM and then into Terraform. * `domain_validaton_options` - (Optional) Contains information about the initial validation of each domain name that occurs. This is an array of maps that contains information about which validation_domain to use for domains in the subject_alternative_names list. - + * `domain_name` - (Required) A fully qualified domain name (FQDN) in the certificate. For example, www.example.com or example.com . + * `validation_domain` - (Required) The domain name that ACM used to send domain validation emails * `tags` - (Optional) A mapping of tags to assign to the resource. -Domain Validation Options objects accept the following attributes - -* `domain_name` - (Required) A fully qualified domain name (FQDN) in the certificate. For example, www.example.com or example.com . -* `validation_domain` - (Required) The domain name that ACM used to send domain validation emails ## Attributes Reference @@ -83,7 +80,7 @@ The following additional attributes are exported: * `arn` - The ARN of the certificate * `certificate_details` - A list of attributes to feed into other resources to complete certificate validation. Can have more than one element, e.g. if SANs are defined. -Certificate Details objects export the following attributes: +Certficiate_detials objects export the following attributes: * `domain_name` - A fully qualified domain name (FQDN) in the certificate. For example, www.example.com or example.com . * `resource_record_name` - The name of the DNS record to create in your domain. This is supplied by ACM. From 97a5cc9fe093ef34395ae4eb80f110c92e691a17 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 26 Apr 2022 14:41:37 -0400 Subject: [PATCH 08/13] Revert "update documententation" This reverts commit e1628683e55ff1bffebf61eb10543eb3b4a3e8f4. --- main.go | 2 +- website/docs/r/acm_certificate.html.markdown | 48 ++++--------------- .../acm_certificate_validation.html.markdown | 6 +-- 3 files changed, 13 insertions(+), 43 deletions(-) diff --git a/main.go b/main.go index 5c4c1d8c0e0..4d56a352b8d 100644 --- a/main.go +++ b/main.go @@ -2,7 +2,7 @@ package main import ( "github.com/hashicorp/terraform/plugin" - "github.com/swinkler/terraform-provider-aws/aws" + "github.com/terraform-providers/terraform-provider-aws/aws" ) func main() { diff --git a/website/docs/r/acm_certificate.html.markdown b/website/docs/r/acm_certificate.html.markdown index c248255f4cc..4236a860f07 100644 --- a/website/docs/r/acm_certificate.html.markdown +++ b/website/docs/r/acm_certificate.html.markdown @@ -33,62 +33,32 @@ resource "aws_acm_certificate" "cert" { Environment = "test" } } - -#example with subject_alternative_names and domain_validation_options -resource "aws_acm_certificate" "cert" { - domain_name = "yolo.example.io" - validation_method = "EMAIL" - subject_alternative_names = ["app1.yolo.example.io", "yolo.example.io"] - - domain_validation_options = [ - { - domain_name = "yolo.example.io" - validation_domain = "example.io" - }, - { - domain_name = "app1.mytest.rd.elliemae.io" - validation_domain = "example.io" - }, - ] -} - -#basic example -resource "aws_acm_certificate" "cert" { - domain_name = "yolo.example.io" - validation_method = "EMAIL" -} ``` ## Argument Reference The following arguments are supported: -* `domain_name` - (Required) A fully qualified domain name (FQDN) in the certificate. For example, www.example.com or example.com . -* `subject_alternative_names` - (Optional) One or more domain names (subject alternative names) included in the certificate. This list contains the domain names that are bound to the public key that is contained in the certificate. The subject alternative names include the canonical domain name (CN) of the certificate and additional domain names that can be used to connect to the website. +* `domain_name` - (Required) A domain name for which the certificate should be issued +* `subject_alternative_names` - (Optional) A list of domains that should be SANs in the issued certificate * `validation_method` - (Required) Which method to use for validation. `DNS` or `EMAIL` are valid, `NONE` can be used for certificates that were imported into ACM and then into Terraform. -* `domain_validaton_options` - (Optional) Contains information about the initial validation of each domain name that occurs. This is an array of maps that contains information about which validation_domain to use for domains in the subject_alternative_names list. - * `domain_name` - (Required) A fully qualified domain name (FQDN) in the certificate. For example, www.example.com or example.com . - * `validation_domain` - (Required) The domain name that ACM used to send domain validation emails * `tags` - (Optional) A mapping of tags to assign to the resource. - ## Attributes Reference The following additional attributes are exported: * `id` - The ARN of the certificate * `arn` - The ARN of the certificate -* `certificate_details` - A list of attributes to feed into other resources to complete certificate validation. Can have more than one element, e.g. if SANs are defined. +* `domain_validation_options` - A list of attributes to feed into other resources to complete certificate validation. Can have more than one element, e.g. if SANs are defined. Only set if `DNS`-validation was used. +* `validation_emails` - A list of addresses that received a validation E-Mail. Only set if `EMAIL`-validation was used. -Certficiate_detials objects export the following attributes: +Domain validation objects export the following attributes: -* `domain_name` - A fully qualified domain name (FQDN) in the certificate. For example, www.example.com or example.com . -* `resource_record_name` - The name of the DNS record to create in your domain. This is supplied by ACM. -* `resource_record_type` - The type of DNS record. Currently this can be CNAME . -* `resource_record_value` - The value of the CNAME record to add to your DNS database. This is supplied by ACM. -* `validation_domain` - The domain name that ACM used to send domain validation emails. -* `validation_method` - One of EMAIl or DNS -* `validation_emails` - A list of email addresses that ACM used to send domain validation emails. +* `domain_name` - The domain to be validated +* `resource_record_name` - The name of the DNS record to create to validate the certificate +* `resource_record_type` - The type of DNS record to create +* `resource_record_value` - The value the DNS record needs to have ## Import diff --git a/website/docs/r/acm_certificate_validation.html.markdown b/website/docs/r/acm_certificate_validation.html.markdown index 9ffcc428035..9278da40655 100644 --- a/website/docs/r/acm_certificate_validation.html.markdown +++ b/website/docs/r/acm_certificate_validation.html.markdown @@ -34,10 +34,10 @@ data "aws_route53_zone" "zone" { } resource "aws_route53_record" "cert_validation" { - name = "${aws_acm_certificate.cert.certificate_details.0.resource_record_name}" - type = "${aws_acm_certificate.cert.certificate_details.0.resource_record_type}" + name = "${aws_acm_certificate.cert.domain_validation_options.0.resource_record_name}" + type = "${aws_acm_certificate.cert.domain_validation_options.0.resource_record_type}" zone_id = "${data.aws_route53_zone.zone.id}" - records = ["${aws_acm_certificate.cert.certificate_details.0.resource_record_value}"] + records = ["${aws_acm_certificate.cert.domain_validation_options.0.resource_record_value}"] ttl = 60 } From 6ced308c73bafe388c852b778fb8ab08ac3a4c49 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 26 Apr 2022 14:41:46 -0400 Subject: [PATCH 09/13] Revert "tests passing" This reverts commit 2be6f70989c2a51f6f5dba7267a600bc09ad5ec4. --- aws/resource_aws_acm_certificate.go | 108 +++++++---------------- aws/resource_aws_acm_certificate_test.go | 38 +++----- 2 files changed, 44 insertions(+), 102 deletions(-) diff --git a/aws/resource_aws_acm_certificate.go b/aws/resource_aws_acm_certificate.go index 69b9b221729..a2e9adec197 100644 --- a/aws/resource_aws_acm_certificate.go +++ b/aws/resource_aws_acm_certificate.go @@ -138,11 +138,11 @@ func resourceAwsAcmCertificateCreate(d *schema.ResourceData, meta interface{}) e params.SubjectAlternativeNames = expandStringList(sanStrings) } - value, ok := d.GetOk("domain_validation_options") + domainValidationOptionsInput, ok := d.GetOk("domain_validation_options") if ok { var domainValidationOptions []*acm.DomainValidationOption - for _, o := range value.([]interface{}) { + for _, o := range domainValidationOptionsInput.([]interface{}) { x := o.(map[string]interface{}) dn := x["domain_name"].(string) vd := x["validation_domain"].(string) @@ -196,13 +196,8 @@ func resourceAwsAcmCertificateRead(d *schema.ResourceData, meta interface{}) err return resource.NonRetryableError(fmt.Errorf("Error describing certificate: %s", err)) } - if err := d.Set("domain_name", resp.Certificate.DomainName); err != nil { - return resource.NonRetryableError(err) - } - - if err := d.Set("arn", resp.Certificate.CertificateArn); err != nil { - return resource.NonRetryableError(err) - } + d.Set("domain_name", resp.Certificate.DomainName) + d.Set("arn", resp.Certificate.CertificateArn) if err := d.Set("subject_alternative_names", cleanUpSubjectAlternativeNames(resp.Certificate)); err != nil { return resource.NonRetryableError(err) @@ -210,14 +205,14 @@ func resourceAwsAcmCertificateRead(d *schema.ResourceData, meta interface{}) err certificateDetails, err := convertCertificateDetails(resp.Certificate) - if err != nil { - return resource.RetryableError(err) - } - if len(certificateDetails) < 1 { return resource.NonRetryableError(fmt.Errorf("Error getting certificate details")) } + if err != nil { + return resource.RetryableError(err) + } + if err := d.Set("certificate_details", certificateDetails); err != nil { return resource.NonRetryableError(err) } @@ -233,34 +228,6 @@ func resourceAwsAcmCertificateRead(d *schema.ResourceData, meta interface{}) err return resource.NonRetryableError(err) } - //support for deprecated attributes - d.Set("validation_emails", certificateDetails[0]["validation_emails"]) - - value, ok := d.GetOk("domain_validation_options") - var domainValidationOptionsInput []interface{} - if ok { - domainValidationOptionsInput = value.([]interface{}) - //pad until it is the same size as certificate details - for len(domainValidationOptionsInput) < len(certificateDetails) { - domainValidationOptionsInput = append(domainValidationOptionsInput, make(map[string]interface{}, 1)) - } - } else { - for i := 0; i < len(certificateDetails); i++ { - domainValidationOptionsInput = append(domainValidationOptionsInput, make(map[string]interface{}, 1)) - } - } - for i, v := range domainValidationOptionsInput { - validationOption := v.(map[string]interface{}) - validationOption["domain_name"] = certificateDetails[i]["domain_name"] - validationOption["resource_record_name"] = certificateDetails[i]["resource_record_name"] - validationOption["resource_record_type"] = certificateDetails[i]["resource_record_type"] - validationOption["resource_record_value"] = certificateDetails[i]["resource_record_value"] - } - - if err := d.Set("domain_validation_options", domainValidationOptionsInput); err != nil { - return resource.NonRetryableError(err) - } - return nil }) } @@ -293,32 +260,31 @@ func convertCertificateDetails(certificate *acm.CertificateDetail) ([]map[string if *certificate.Type == acm.CertificateTypeAmazonIssued { for _, o := range certificate.DomainValidationOptions { - var validationOption map[string]interface{} + var resourceRecordName interface{} + var resourceRecordType interface{} + var resourceRecordValue interface{} + var validationMethod interface{} if o.ResourceRecord != nil { - validationOption = map[string]interface{}{ - "domain_name": *o.DomainName, - "resource_record_name": *o.ResourceRecord.Name, - "resource_record_type": *o.ResourceRecord.Type, - "resource_record_value": *o.ResourceRecord.Value, - "validation_method": *o.ValidationMethod, - } - - } else if o.ValidationEmails != nil && len(o.ValidationEmails) > 0 { - var validationEmails []string - for _, email := range o.ValidationEmails { - validationEmails = append(validationEmails, *email) - } - validationOption = map[string]interface{}{ - "domain_name": *o.DomainName, - "validation_emails": validationEmails, - "validation_method": "EMAIL", - } - } else { - return nil, fmt.Errorf("Validation options not yet updated. Need to retry: %#v", o) + resourceRecordName = *o.ResourceRecord.Name + resourceRecordType = *o.ResourceRecord.Type + resourceRecordValue = *o.ResourceRecord.Value + } + if o.ValidationMethod != nil { + validationMethod = *o.ValidationMethod } - if o.ValidationDomain != nil { - validationOption["validation_domain"] = *o.ValidationDomain + var validationEmails []string + for _, email := range o.ValidationEmails { + validationEmails = append(validationEmails, *email) + } + validationOption := map[string]interface{}{ + "domain_name": *o.DomainName, + "validation_domain": *o.ValidationDomain, + "resource_record_name": resourceRecordName, + "resource_record_type": resourceRecordType, + "resource_record_value": resourceRecordValue, + "validation_emails": validationEmails, + "validation_method": validationMethod, } certificateDetails = append(certificateDetails, validationOption) } @@ -329,23 +295,11 @@ func convertCertificateDetails(certificate *acm.CertificateDetail) ([]map[string func resourceAwsAcmCertificateDelete(d *schema.ResourceData, meta interface{}) error { acmconn := meta.(*AWSClient).acmconn - log.Printf("[INFO] Deleting ACM Certificate: %s", d.Id()) - params := &acm.DeleteCertificateInput{ CertificateArn: aws.String(d.Id()), } - err := resource.Retry(10*time.Minute, func() *resource.RetryError { - _, err := acmconn.DeleteCertificate(params) - if err != nil { - if isAWSErr(err, acm.ErrCodeResourceInUseException, "") { - log.Printf("[WARN] Conflict deleting certificate in use: %s, retrying", err.Error()) - return resource.RetryableError(err) - } - return resource.NonRetryableError(err) - } - return nil - }) + _, err := acmconn.DeleteCertificate(params) if err != nil && !isAWSErr(err, acm.ErrCodeResourceNotFoundException, "") { return fmt.Errorf("Error deleting certificate: %s", err) diff --git a/aws/resource_aws_acm_certificate_test.go b/aws/resource_aws_acm_certificate_test.go index 0347b592d5b..24f7b956441 100644 --- a/aws/resource_aws_acm_certificate_test.go +++ b/aws/resource_aws_acm_certificate_test.go @@ -44,12 +44,11 @@ func TestAccAWSAcmCertificate_emailValidation(t *testing.T) { CheckDestroy: testAccCheckAcmCertificateDestroy, Steps: []resource.TestStep{ resource.TestStep{ - Config: testAccAcmCertificateConfig(domain, acm.ValidationMethodEmail), - ExpectNonEmptyPlan: true, + Config: testAccAcmCertificateConfig(domain, acm.ValidationMethodEmail), Check: resource.ComposeTestCheckFunc( resource.TestMatchResourceAttr("aws_acm_certificate.cert", "arn", certificateArnRegex), resource.TestCheckResourceAttr("aws_acm_certificate.cert", "domain_name", domain), - resource.TestCheckResourceAttr("aws_acm_certificate.cert", "domain_validation_options.#", "1"), + resource.TestCheckResourceAttr("aws_acm_certificate.cert", "domain_validation_options.#", "0"), resource.TestCheckResourceAttr("aws_acm_certificate.cert", "subject_alternative_names.#", "0"), resource.TestMatchResourceAttr("aws_acm_certificate.cert", "validation_emails.0", regexp.MustCompile(`^[^@]+@.+$`)), resource.TestCheckResourceAttr("aws_acm_certificate.cert", "validation_method", acm.ValidationMethodEmail), @@ -78,8 +77,7 @@ func TestAccAWSAcmCertificate_dnsValidation(t *testing.T) { CheckDestroy: testAccCheckAcmCertificateDestroy, Steps: []resource.TestStep{ resource.TestStep{ - Config: testAccAcmCertificateConfig(domain, acm.ValidationMethodDns), - ExpectNonEmptyPlan: true, + Config: testAccAcmCertificateConfig(domain, acm.ValidationMethodDns), Check: resource.ComposeTestCheckFunc( resource.TestMatchResourceAttr("aws_acm_certificate.cert", "arn", certificateArnRegex), resource.TestCheckResourceAttr("aws_acm_certificate.cert", "domain_name", domain), @@ -111,8 +109,7 @@ func TestAccAWSAcmCertificate_root(t *testing.T) { CheckDestroy: testAccCheckAcmCertificateDestroy, Steps: []resource.TestStep{ resource.TestStep{ - Config: testAccAcmCertificateConfig(rootDomain, acm.ValidationMethodDns), - ExpectNonEmptyPlan: true, + Config: testAccAcmCertificateConfig(rootDomain, acm.ValidationMethodDns), Check: resource.ComposeTestCheckFunc( resource.TestMatchResourceAttr("aws_acm_certificate.cert", "arn", certificateArnRegex), resource.TestCheckResourceAttr("aws_acm_certificate.cert", "domain_name", rootDomain), @@ -145,8 +142,7 @@ func TestAccAWSAcmCertificate_rootAndWildcardSan(t *testing.T) { CheckDestroy: testAccCheckAcmCertificateDestroy, Steps: []resource.TestStep{ resource.TestStep{ - Config: testAccAcmCertificateConfig_subjectAlternativeNames(rootDomain, strconv.Quote(wildcardDomain), acm.ValidationMethodDns), - ExpectNonEmptyPlan: true, + Config: testAccAcmCertificateConfig_subjectAlternativeNames(rootDomain, strconv.Quote(wildcardDomain), acm.ValidationMethodDns), Check: resource.ComposeTestCheckFunc( resource.TestMatchResourceAttr("aws_acm_certificate.cert", "arn", certificateArnRegex), resource.TestCheckResourceAttr("aws_acm_certificate.cert", "domain_name", rootDomain), @@ -188,8 +184,7 @@ func TestAccAWSAcmCertificate_san_single(t *testing.T) { CheckDestroy: testAccCheckAcmCertificateDestroy, Steps: []resource.TestStep{ resource.TestStep{ - Config: testAccAcmCertificateConfig_subjectAlternativeNames(domain, strconv.Quote(sanDomain), acm.ValidationMethodDns), - ExpectNonEmptyPlan: true, + Config: testAccAcmCertificateConfig_subjectAlternativeNames(domain, strconv.Quote(sanDomain), acm.ValidationMethodDns), Check: resource.ComposeTestCheckFunc( resource.TestMatchResourceAttr("aws_acm_certificate.cert", "arn", certificateArnRegex), resource.TestCheckResourceAttr("aws_acm_certificate.cert", "domain_name", domain), @@ -232,8 +227,7 @@ func TestAccAWSAcmCertificate_san_multiple(t *testing.T) { CheckDestroy: testAccCheckAcmCertificateDestroy, Steps: []resource.TestStep{ resource.TestStep{ - Config: testAccAcmCertificateConfig_subjectAlternativeNames(domain, fmt.Sprintf("%q, %q", sanDomain1, sanDomain2), acm.ValidationMethodDns), - ExpectNonEmptyPlan: true, + Config: testAccAcmCertificateConfig_subjectAlternativeNames(domain, fmt.Sprintf("%q, %q", sanDomain1, sanDomain2), acm.ValidationMethodDns), Check: resource.ComposeTestCheckFunc( resource.TestMatchResourceAttr("aws_acm_certificate.cert", "arn", certificateArnRegex), resource.TestCheckResourceAttr("aws_acm_certificate.cert", "domain_name", domain), @@ -276,8 +270,7 @@ func TestAccAWSAcmCertificate_wildcard(t *testing.T) { CheckDestroy: testAccCheckAcmCertificateDestroy, Steps: []resource.TestStep{ resource.TestStep{ - Config: testAccAcmCertificateConfig(wildcardDomain, acm.ValidationMethodDns), - ExpectNonEmptyPlan: true, + Config: testAccAcmCertificateConfig(wildcardDomain, acm.ValidationMethodDns), Check: resource.ComposeTestCheckFunc( resource.TestMatchResourceAttr("aws_acm_certificate.cert", "arn", certificateArnRegex), resource.TestCheckResourceAttr("aws_acm_certificate.cert", "domain_name", wildcardDomain), @@ -310,8 +303,7 @@ func TestAccAWSAcmCertificate_wildcardAndRootSan(t *testing.T) { CheckDestroy: testAccCheckAcmCertificateDestroy, Steps: []resource.TestStep{ resource.TestStep{ - Config: testAccAcmCertificateConfig_subjectAlternativeNames(wildcardDomain, strconv.Quote(rootDomain), acm.ValidationMethodDns), - ExpectNonEmptyPlan: true, + Config: testAccAcmCertificateConfig_subjectAlternativeNames(wildcardDomain, strconv.Quote(rootDomain), acm.ValidationMethodDns), Check: resource.ComposeTestCheckFunc( resource.TestMatchResourceAttr("aws_acm_certificate.cert", "arn", certificateArnRegex), resource.TestCheckResourceAttr("aws_acm_certificate.cert", "domain_name", wildcardDomain), @@ -352,15 +344,13 @@ func TestAccAWSAcmCertificate_tags(t *testing.T) { CheckDestroy: testAccCheckAcmCertificateDestroy, Steps: []resource.TestStep{ resource.TestStep{ - Config: testAccAcmCertificateConfig(domain, acm.ValidationMethodDns), - ExpectNonEmptyPlan: true, + Config: testAccAcmCertificateConfig(domain, acm.ValidationMethodDns), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr("aws_acm_certificate.cert", "tags.%", "0"), ), }, resource.TestStep{ - Config: testAccAcmCertificateConfig_twoTags(domain, acm.ValidationMethodDns, "Hello", "World", "Foo", "Bar"), - ExpectNonEmptyPlan: true, + Config: testAccAcmCertificateConfig_twoTags(domain, acm.ValidationMethodDns, "Hello", "World", "Foo", "Bar"), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr("aws_acm_certificate.cert", "tags.%", "2"), resource.TestCheckResourceAttr("aws_acm_certificate.cert", "tags.Hello", "World"), @@ -368,8 +358,7 @@ func TestAccAWSAcmCertificate_tags(t *testing.T) { ), }, resource.TestStep{ - Config: testAccAcmCertificateConfig_twoTags(domain, acm.ValidationMethodDns, "Hello", "World", "Foo", "Baz"), - ExpectNonEmptyPlan: true, + Config: testAccAcmCertificateConfig_twoTags(domain, acm.ValidationMethodDns, "Hello", "World", "Foo", "Baz"), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr("aws_acm_certificate.cert", "tags.%", "2"), resource.TestCheckResourceAttr("aws_acm_certificate.cert", "tags.Hello", "World"), @@ -377,8 +366,7 @@ func TestAccAWSAcmCertificate_tags(t *testing.T) { ), }, resource.TestStep{ - Config: testAccAcmCertificateConfig_oneTag(domain, acm.ValidationMethodDns, "Environment", "Test"), - ExpectNonEmptyPlan: true, + Config: testAccAcmCertificateConfig_oneTag(domain, acm.ValidationMethodDns, "Environment", "Test"), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr("aws_acm_certificate.cert", "tags.%", "1"), resource.TestCheckResourceAttr("aws_acm_certificate.cert", "tags.Environment", "Test"), From 5ae16a9c4b1a3d56039da37579fa58cf01faff74 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 26 Apr 2022 14:41:56 -0400 Subject: [PATCH 10/13] Revert "add deprecated message for removed attributes" This reverts commit 61220e97fe4a508cbf9dd6dd30db0f6a50b6c2b0. --- aws/resource_aws_acm_certificate.go | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/aws/resource_aws_acm_certificate.go b/aws/resource_aws_acm_certificate.go index a2e9adec197..979bd0f2197 100644 --- a/aws/resource_aws_acm_certificate.go +++ b/aws/resource_aws_acm_certificate.go @@ -54,21 +54,6 @@ func resourceAwsAcmCertificate() *schema.Resource { Required: true, ForceNew: true, }, - "resource_record_name": { - Type: schema.TypeString, - Computed: true, - Deprecated: "Use `certificate_details[0].resource_record_name` instead", - }, - "resource_record_type": { - Type: schema.TypeString, - Computed: true, - Deprecated: "Use `certificate_details[0].resource_record_type` instead", - }, - "resource_record_value": { - Type: schema.TypeString, - Computed: true, - Deprecated: "Use `certificate_details[0].resource_record_value` instead", - }, }, }, }, @@ -114,12 +99,6 @@ func resourceAwsAcmCertificate() *schema.Resource { }, }, "tags": tagsSchema(), - "validation_emails": { - Type: schema.TypeList, - Computed: true, - Elem: &schema.Schema{Type: schema.TypeString}, - Deprecated: "Use `certificate_details[0].validation_emails` instead", - }, }, } } From 8524000e434eccdbcf3c269ecb869ec40ab0f72b Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 26 Apr 2022 14:42:06 -0400 Subject: [PATCH 11/13] Revert "change the interface of aws_acm_certificate to make more sense" This reverts commit 30bdfdb179897ab8ab516494113e21383bb4c944. --- aws/resource_aws_acm_certificate.go | 128 ++++++++++++---------------- 1 file changed, 55 insertions(+), 73 deletions(-) diff --git a/aws/resource_aws_acm_certificate.go b/aws/resource_aws_acm_certificate.go index 979bd0f2197..66f6bf6cac1 100644 --- a/aws/resource_aws_acm_certificate.go +++ b/aws/resource_aws_acm_certificate.go @@ -38,30 +38,16 @@ func resourceAwsAcmCertificate() *schema.Resource { Required: true, ForceNew: true, }, - "domain_validation_options": { - Type: schema.TypeList, + "validation_domain": { + Type: schema.TypeString, Optional: true, ForceNew: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "domain_name": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - "validation_domain": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - }, - }, }, "arn": { Type: schema.TypeString, Computed: true, }, - "certificate_details": { + "domain_validation_options": { Type: schema.TypeList, Computed: true, Elem: &schema.Resource{ @@ -86,18 +72,14 @@ func resourceAwsAcmCertificate() *schema.Resource { Type: schema.TypeString, Computed: true, }, - "validation_method": { - Type: schema.TypeString, - Computed: true, - }, - "validation_emails": { - Type: schema.TypeList, - Computed: true, - Elem: &schema.Schema{Type: schema.TypeString}, - }, }, }, }, + "validation_emails": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, "tags": tagsSchema(), }, } @@ -117,20 +99,13 @@ func resourceAwsAcmCertificateCreate(d *schema.ResourceData, meta interface{}) e params.SubjectAlternativeNames = expandStringList(sanStrings) } - domainValidationOptionsInput, ok := d.GetOk("domain_validation_options") - + validationDomain, ok := d.GetOk("validation_domain") if ok { - var domainValidationOptions []*acm.DomainValidationOption - for _, o := range domainValidationOptionsInput.([]interface{}) { - x := o.(map[string]interface{}) - dn := x["domain_name"].(string) - vd := x["validation_domain"].(string) - domainValidationOption := &acm.DomainValidationOption{ - DomainName: &dn, - ValidationDomain: &vd, - } - domainValidationOptions = append(domainValidationOptions, domainValidationOption) + domainValidationOption := &acm.DomainValidationOption{ + DomainName: aws.String(domainName), + ValidationDomain: aws.String(validationDomain.(string)), } + domainValidationOptions := []*acm.DomainValidationOption{domainValidationOption} params.SetDomainValidationOptions(domainValidationOptions) } @@ -182,21 +157,25 @@ func resourceAwsAcmCertificateRead(d *schema.ResourceData, meta interface{}) err return resource.NonRetryableError(err) } - certificateDetails, err := convertCertificateDetails(resp.Certificate) - - if len(certificateDetails) < 1 { - return resource.NonRetryableError(fmt.Errorf("Error getting certificate details")) - } + domainValidationOptions, emailValidationOptions, err := convertValidationOptions(resp.Certificate) if err != nil { return resource.RetryableError(err) } - if err := d.Set("certificate_details", certificateDetails); err != nil { + if err := d.Set("domain_validation_options", domainValidationOptions); err != nil { + return resource.NonRetryableError(err) + } + if err := d.Set("validation_emails", emailValidationOptions); err != nil { return resource.NonRetryableError(err) } + if len(domainValidationOptions) > 0 { + d.Set("validation_domain", resp.Certificate.DomainValidationOptions[0].ValidationDomain) + } else { + d.Set("validation_domain", resp.Certificate.DomainName) + } - d.Set("validation_method", certificateDetails[0]["validation_method"]) + d.Set("validation_method", resourceAwsAcmCertificateGuessValidationMethod(domainValidationOptions, emailValidationOptions)) params := &acm.ListTagsForCertificateInput{ CertificateArn: aws.String(d.Id()), @@ -210,6 +189,17 @@ func resourceAwsAcmCertificateRead(d *schema.ResourceData, meta interface{}) err return nil }) } +func resourceAwsAcmCertificateGuessValidationMethod(domainValidationOptions []map[string]interface{}, emailValidationOptions []string) string { + // The DescribeCertificate Response doesn't have information on what validation method was used + // so we need to guess from the validation options we see... + if len(domainValidationOptions) > 0 { + return acm.ValidationMethodDns + } else if len(emailValidationOptions) > 0 { + return acm.ValidationMethodEmail + } else { + return "NONE" + } +} func resourceAwsAcmCertificateUpdate(d *schema.ResourceData, meta interface{}) error { if d.HasChange("tags") { @@ -234,41 +224,33 @@ func cleanUpSubjectAlternativeNames(cert *acm.CertificateDetail) []string { } -func convertCertificateDetails(certificate *acm.CertificateDetail) ([]map[string]interface{}, error) { - var certificateDetails []map[string]interface{} +func convertValidationOptions(certificate *acm.CertificateDetail) ([]map[string]interface{}, []string, error) { + var domainValidationResult []map[string]interface{} + var emailValidationResult []string if *certificate.Type == acm.CertificateTypeAmazonIssued { for _, o := range certificate.DomainValidationOptions { - var resourceRecordName interface{} - var resourceRecordType interface{} - var resourceRecordValue interface{} - var validationMethod interface{} if o.ResourceRecord != nil { - resourceRecordName = *o.ResourceRecord.Name - resourceRecordType = *o.ResourceRecord.Type - resourceRecordValue = *o.ResourceRecord.Value - } - if o.ValidationMethod != nil { - validationMethod = *o.ValidationMethod + validationOption := map[string]interface{}{ + "domain_name": *o.DomainName, + "validation_domain": *o.ValidationDomain, + "resource_record_name": *o.ResourceRecord.Name, + "resource_record_type": *o.ResourceRecord.Type, + "resource_record_value": *o.ResourceRecord.Value, + } + domainValidationResult = append(domainValidationResult, validationOption) + } else if o.ValidationEmails != nil && len(o.ValidationEmails) > 0 { + for _, validationEmail := range o.ValidationEmails { + emailValidationResult = append(emailValidationResult, *validationEmail) + } + } else { + log.Printf("[DEBUG] No validation options need to retry: %#v", o) + return nil, nil, fmt.Errorf("No validation options need to retry: %#v", o) } - - var validationEmails []string - for _, email := range o.ValidationEmails { - validationEmails = append(validationEmails, *email) - } - validationOption := map[string]interface{}{ - "domain_name": *o.DomainName, - "validation_domain": *o.ValidationDomain, - "resource_record_name": resourceRecordName, - "resource_record_type": resourceRecordType, - "resource_record_value": resourceRecordValue, - "validation_emails": validationEmails, - "validation_method": validationMethod, - } - certificateDetails = append(certificateDetails, validationOption) } } - return certificateDetails, nil + + return domainValidationResult, emailValidationResult, nil } func resourceAwsAcmCertificateDelete(d *schema.ResourceData, meta interface{}) error { From 4dd73795792308e884e5f6ac167b108bdb0073f8 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 26 Apr 2022 14:42:15 -0400 Subject: [PATCH 12/13] Revert "add validation_domain option to aws_acm_certificate" This reverts commit c291b48625669bd014423d673f77caee7db82ad4. --- aws/resource_aws_acm_certificate.go | 29 +---------------------------- 1 file changed, 1 insertion(+), 28 deletions(-) diff --git a/aws/resource_aws_acm_certificate.go b/aws/resource_aws_acm_certificate.go index 66f6bf6cac1..4b2c06b3624 100644 --- a/aws/resource_aws_acm_certificate.go +++ b/aws/resource_aws_acm_certificate.go @@ -38,11 +38,6 @@ func resourceAwsAcmCertificate() *schema.Resource { Required: true, ForceNew: true, }, - "validation_domain": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - }, "arn": { Type: schema.TypeString, Computed: true, @@ -68,10 +63,6 @@ func resourceAwsAcmCertificate() *schema.Resource { Type: schema.TypeString, Computed: true, }, - "validation_domain": { - Type: schema.TypeString, - Computed: true, - }, }, }, }, @@ -87,9 +78,8 @@ func resourceAwsAcmCertificate() *schema.Resource { func resourceAwsAcmCertificateCreate(d *schema.ResourceData, meta interface{}) error { acmconn := meta.(*AWSClient).acmconn - domainName := d.Get("domain_name").(string) params := &acm.RequestCertificateInput{ - DomainName: aws.String(domainName), + DomainName: aws.String(d.Get("domain_name").(string)), ValidationMethod: aws.String(d.Get("validation_method").(string)), } @@ -99,16 +89,6 @@ func resourceAwsAcmCertificateCreate(d *schema.ResourceData, meta interface{}) e params.SubjectAlternativeNames = expandStringList(sanStrings) } - validationDomain, ok := d.GetOk("validation_domain") - if ok { - domainValidationOption := &acm.DomainValidationOption{ - DomainName: aws.String(domainName), - ValidationDomain: aws.String(validationDomain.(string)), - } - domainValidationOptions := []*acm.DomainValidationOption{domainValidationOption} - params.SetDomainValidationOptions(domainValidationOptions) - } - log.Printf("[DEBUG] ACM Certificate Request: %#v", params) resp, err := acmconn.RequestCertificate(params) @@ -169,12 +149,6 @@ func resourceAwsAcmCertificateRead(d *schema.ResourceData, meta interface{}) err if err := d.Set("validation_emails", emailValidationOptions); err != nil { return resource.NonRetryableError(err) } - if len(domainValidationOptions) > 0 { - d.Set("validation_domain", resp.Certificate.DomainValidationOptions[0].ValidationDomain) - } else { - d.Set("validation_domain", resp.Certificate.DomainName) - } - d.Set("validation_method", resourceAwsAcmCertificateGuessValidationMethod(domainValidationOptions, emailValidationOptions)) params := &acm.ListTagsForCertificateInput{ @@ -233,7 +207,6 @@ func convertValidationOptions(certificate *acm.CertificateDetail) ([]map[string] if o.ResourceRecord != nil { validationOption := map[string]interface{}{ "domain_name": *o.DomainName, - "validation_domain": *o.ValidationDomain, "resource_record_name": *o.ResourceRecord.Name, "resource_record_type": *o.ResourceRecord.Type, "resource_record_value": *o.ResourceRecord.Value, From e47e87875cd6d7f21262a1177ff275796d4586c0 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 26 Apr 2022 15:20:38 -0400 Subject: [PATCH 13/13] r/aws_acm_certificate: Add 'validation_option' argument. --- .changelog/3853.txt | 3 + internal/service/acm/certificate.go | 68 ++++++++++++++++++++ internal/service/acm/certificate_test.go | 60 ++++++++++++++++- website/docs/r/acm_certificate.html.markdown | 22 +++++++ 4 files changed, 151 insertions(+), 2 deletions(-) create mode 100644 .changelog/3853.txt diff --git a/.changelog/3853.txt b/.changelog/3853.txt new file mode 100644 index 00000000000..c860222c167 --- /dev/null +++ b/.changelog/3853.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +resource/aws_acm_certificate: Add `validation_option` argument +``` \ No newline at end of file diff --git a/internal/service/acm/certificate.go b/internal/service/acm/certificate.go index 65ff3826f80..b5999d419ff 100644 --- a/internal/service/acm/certificate.go +++ b/internal/service/acm/certificate.go @@ -177,6 +177,26 @@ func ResourceCertificate() *schema.Resource { ValidateFunc: validation.StringInSlice(append(acm.ValidationMethod_Values(), certificateValidationMethodNone), false), ConflictsWith: []string{"certificate_authority_arn", "certificate_body", "certificate_chain", "private_key"}, }, + "validation_option": { + Type: schema.TypeSet, + Optional: true, + ForceNew: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "domain_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "validation_domain": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + }, + }, + ConflictsWith: []string{"certificate_body", "certificate_chain", "private_key"}, + }, }, CustomizeDiff: customdiff.Sequence( @@ -271,6 +291,10 @@ func resourceCertificateCreate(d *schema.ResourceData, meta interface{}) error { input.ValidationMethod = aws.String(v.(string)) } + if v, ok := d.GetOk("validation_option"); ok && v.(*schema.Set).Len() > 0 { + input.DomainValidationOptions = expandDomainValidationOptions(v.(*schema.Set).List()) + } + if len(tags) > 0 { input.Tags = Tags(tags.IgnoreAWS()) } @@ -486,6 +510,50 @@ func flattenCertificateOptions(apiObject *acm.CertificateOptions) map[string]int return tfMap } +func expandDomainValidationOption(tfMap map[string]interface{}) *acm.DomainValidationOption { + if tfMap == nil { + return nil + } + + apiObject := &acm.DomainValidationOption{} + + if v, ok := tfMap["domain_name"].(string); ok && v != "" { + apiObject.DomainName = aws.String(v) + } + + if v, ok := tfMap["validation_domain"].(string); ok && v != "" { + apiObject.ValidationDomain = aws.String(v) + } + + return apiObject +} + +func expandDomainValidationOptions(tfList []interface{}) []*acm.DomainValidationOption { + if len(tfList) == 0 { + return nil + } + + var apiObjects []*acm.DomainValidationOption + + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + + if !ok { + continue + } + + apiObject := expandDomainValidationOption(tfMap) + + if apiObject == nil { + continue + } + + apiObjects = append(apiObjects, apiObject) + } + + return apiObjects +} + func flattenDomainValidation(apiObject *acm.DomainValidation) (map[string]interface{}, []string) { if apiObject == nil { return nil, nil diff --git a/internal/service/acm/certificate_test.go b/internal/service/acm/certificate_test.go index d78e2317511..73211c2be65 100644 --- a/internal/service/acm/certificate_test.go +++ b/internal/service/acm/certificate_test.go @@ -41,6 +41,7 @@ func TestAccACMCertificate_emailValidation(t *testing.T) { acctest.CheckResourceAttrGreaterThanValue(resourceName, "validation_emails.#", "0"), resource.TestMatchResourceAttr(resourceName, "validation_emails.0", regexp.MustCompile(`^[^@]+@.+$`)), resource.TestCheckResourceAttr(resourceName, "validation_method", acm.ValidationMethodEmail), + resource.TestCheckResourceAttr(resourceName, "validation_option.#", "0"), ), }, { @@ -80,6 +81,7 @@ func TestAccACMCertificate_dnsValidation(t *testing.T) { resource.TestCheckTypeSetElemAttr(resourceName, "subject_alternative_names.*", domain), resource.TestCheckResourceAttr(resourceName, "validation_emails.#", "0"), resource.TestCheckResourceAttr(resourceName, "validation_method", acm.ValidationMethodDns), + resource.TestCheckResourceAttr(resourceName, "validation_option.#", "0"), ), }, { @@ -118,6 +120,7 @@ func TestAccACMCertificate_root(t *testing.T) { resource.TestCheckTypeSetElemAttr(resourceName, "subject_alternative_names.*", rootDomain), resource.TestCheckResourceAttr(resourceName, "validation_emails.#", "0"), resource.TestCheckResourceAttr(resourceName, "validation_method", acm.ValidationMethodDns), + resource.TestCheckResourceAttr(resourceName, "validation_option.#", "0"), ), }, { @@ -129,6 +132,44 @@ func TestAccACMCertificate_root(t *testing.T) { }) } +func TestAccACMCertificate_validationOptions(t *testing.T) { + resourceName := "aws_acm_certificate.test" + rootDomain := acctest.ACMCertificateDomainFromEnv(t) + domain := acctest.ACMCertificateRandomSubDomain(rootDomain) + var v acm.CertificateDetail + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, acm.EndpointsID), + Providers: acctest.Providers, + CheckDestroy: testAccCheckAcmCertificateDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAcmCertificateValidationOptionsConfig(rootDomain, domain), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAcmCertificateExists(resourceName, &v), + acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "acm", regexp.MustCompile("certificate/.+$")), + resource.TestCheckResourceAttr(resourceName, "domain_name", domain), + resource.TestCheckResourceAttr(resourceName, "domain_validation_options.#", "0"), + resource.TestCheckResourceAttr(resourceName, "status", acm.CertificateStatusPendingValidation), + resource.TestCheckResourceAttr(resourceName, "subject_alternative_names.#", "1"), + resource.TestCheckTypeSetElemAttr(resourceName, "subject_alternative_names.*", domain), + acctest.CheckResourceAttrGreaterThanValue(resourceName, "validation_emails.#", "0"), + resource.TestMatchResourceAttr(resourceName, "validation_emails.0", regexp.MustCompile(`^[^@]+@.+$`)), + resource.TestCheckResourceAttr(resourceName, "validation_method", acm.ValidationMethodEmail), + resource.TestCheckResourceAttr(resourceName, "validation_option.#", "1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"validation_option"}, + }, + }, + }) +} + func TestAccACMCertificate_privateCert(t *testing.T) { certificateAuthorityResourceName := "aws_acmpca_certificate_authority.test" resourceName := "aws_acm_certificate.test" @@ -154,6 +195,7 @@ func TestAccACMCertificate_privateCert(t *testing.T) { resource.TestCheckTypeSetElemAttr(resourceName, "subject_alternative_names.*", certificateDomainName), resource.TestCheckResourceAttr(resourceName, "validation_emails.#", "0"), resource.TestCheckResourceAttr(resourceName, "validation_method", "NONE"), + resource.TestCheckResourceAttr(resourceName, "validation_option.#", "0"), resource.TestCheckResourceAttrPair(resourceName, "certificate_authority_arn", certificateAuthorityResourceName, "arn"), ), }, @@ -750,12 +792,26 @@ func testAccCheckAcmCertificateDestroy(s *terraform.State) error { func testAccAcmCertificateConfig(domainName, validationMethod string) string { return fmt.Sprintf(` resource "aws_acm_certificate" "test" { - domain_name = "%s" - validation_method = "%s" + domain_name = %[1]q + validation_method = %[2]q } `, domainName, validationMethod) } +func testAccAcmCertificateValidationOptionsConfig(rootDomainName, domainName string) string { + return fmt.Sprintf(` +resource "aws_acm_certificate" "test" { + domain_name = %[2]q + validation_method = "EMAIL" + + validation_option { + domain_name = %[2]q + validation_domain = %[1]q + } +} +`, rootDomainName, domainName) +} + func testAccAcmCertificatePrivateCertConfig(commonName, certificateDomainName string) string { return fmt.Sprintf(` resource "aws_acmpca_certificate_authority" "test" { diff --git a/website/docs/r/acm_certificate.html.markdown b/website/docs/r/acm_certificate.html.markdown index 2211c6a2fee..1a532f9ec34 100644 --- a/website/docs/r/acm_certificate.html.markdown +++ b/website/docs/r/acm_certificate.html.markdown @@ -45,6 +45,20 @@ resource "aws_acm_certificate" "cert" { } ``` +### Custom Domain Validation Options + +```terraform +resource "aws_acm_certificate" "cert" { + domain_name = "testing.example.com" + validation_method = "EMAIL" + + validation_option { + domain_name = "testing.example.com" + validation_domain = "example.com" + } +} +``` + ### Existing Certificate Body Import ```terraform @@ -108,6 +122,7 @@ The following arguments are supported: * `subject_alternative_names` - (Optional) Set of domains that should be SANs in the issued certificate. To remove all elements of a previously configured list, set this value equal to an empty list (`[]`) or use the [`terraform taint` command](https://www.terraform.io/docs/commands/taint.html) to trigger recreation. * `validation_method` - (Required) Which method to use for validation. `DNS` or `EMAIL` are valid, `NONE` can be used for certificates that were imported into ACM and then into Terraform. * `options` - (Optional) Configuration block used to set certificate options. Detailed below. + * `validation_option` - (Optional) Configuration block used to specify information about the initial validation of each domain name. Detailed below. * Importing an existing certificate * `private_key` - (Required) The certificate's PEM-formatted private key * `certificate_body` - (Required) The certificate's PEM-formatted public key @@ -124,6 +139,13 @@ Supported nested arguments for the `options` configuration block: * `certificate_transparency_logging_preference` - (Optional) Specifies whether certificate details should be added to a certificate transparency log. Valid values are `ENABLED` or `DISABLED`. See https://docs.aws.amazon.com/acm/latest/userguide/acm-concepts.html#concept-transparency for more details. +## validation_option Configuration Block + +Supported nested arguments for the `validation_option` configuration block: + +* `domain_name` - (Required) A fully qualified domain name (FQDN) in the certificate. +* `validation_domain` - (Required) The domain name that you want ACM to use to send you validation emails. This domain name is the suffix of the email addresses that you want ACM to use. This must be the same as the `domain_name` value or a superdomain of the `domain_name` value. For example, if you request a certificate for `"testing.example.com"`, you can specify `"example.com"` for this value. + ## Attributes Reference In addition to all arguments above, the following attributes are exported: