Skip to content

Commit

Permalink
Merge pull request #3853 from scottwinkler/feature/acm-cert
Browse files Browse the repository at this point in the history
Add validation_domain parameter to aws_acm_certificate
  • Loading branch information
ewbankkit authored Apr 26, 2022
2 parents aa09bba + e47e878 commit fc271f6
Show file tree
Hide file tree
Showing 4 changed files with 151 additions and 2 deletions.
3 changes: 3 additions & 0 deletions .changelog/3853.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:enhancement
resource/aws_acm_certificate: Add `validation_option` argument
```
68 changes: 68 additions & 0 deletions internal/service/acm/certificate.go
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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())
}
Expand Down Expand Up @@ -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
Expand Down
60 changes: 58 additions & 2 deletions internal/service/acm/certificate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
),
},
{
Expand Down Expand Up @@ -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"),
),
},
{
Expand Down Expand Up @@ -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"),
),
},
{
Expand All @@ -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"
Expand All @@ -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"),
),
},
Expand Down Expand Up @@ -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" {
Expand Down
22 changes: 22 additions & 0 deletions website/docs/r/acm_certificate.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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:
Expand Down

0 comments on commit fc271f6

Please sign in to comment.