Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add validation_domain parameter to aws_acm_certificate #3853

Merged
merged 14 commits into from
Apr 26, 2022
Merged
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