From 84ac8b9e35a1dc3c3b32ea21115f32d397cebf7c Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 12 Sep 2022 14:16:36 -0400 Subject: [PATCH 01/26] r/aws_acm_certificate: Switch to 'WithoutTimeout' CRUD handler signatures (#15090). --- internal/service/acm/certificate.go | 57 +++++++++++++++-------------- 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/internal/service/acm/certificate.go b/internal/service/acm/certificate.go index bf92ab5c9f0..3254d94c362 100644 --- a/internal/service/acm/certificate.go +++ b/internal/service/acm/certificate.go @@ -15,6 +15,7 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/acm" "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" @@ -40,10 +41,10 @@ const ( func ResourceCertificate() *schema.Resource { return &schema.Resource{ - Create: resourceCertificateCreate, - Read: resourceCertificateRead, - Update: resourceCertificateUpdate, - Delete: resourceCertificateDelete, + CreateWithoutTimeout: resourceCertificateCreate, + ReadWithoutTimeout: resourceCertificateRead, + UpdateWithoutTimeout: resourceCertificateUpdate, + DeleteWithoutTimeout: resourceCertificateDelete, Importer: &schema.ResourceImporter{ State: schema.ImportStatePassthrough, @@ -262,7 +263,7 @@ func ResourceCertificate() *schema.Resource { } } -func resourceCertificateCreate(d *schema.ResourceData, meta interface{}) error { +func resourceCertificateCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*conns.AWSClient).ACMConn defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig tags := defaultTagsConfig.MergeTags(tftags.New(d.Get("tags").(map[string]interface{}))) @@ -272,7 +273,7 @@ func resourceCertificateCreate(d *schema.ResourceData, meta interface{}) error { _, v2 := d.GetOk("validation_method") if !v1 && !v2 { - return errors.New("`certificate_authority_arn` or `validation_method` must be set when creating an ACM certificate") + return diag.FromErr(errors.New("`certificate_authority_arn` or `validation_method` must be set when creating an ACM certificate")) } domainName := d.Get("domain_name").(string) @@ -311,7 +312,7 @@ func resourceCertificateCreate(d *schema.ResourceData, meta interface{}) error { output, err := conn.RequestCertificate(input) if err != nil { - return fmt.Errorf("requesting ACM Certificate (%s): %w", domainName, err) + return diag.Errorf("requesting ACM Certificate (%s): %s", domainName, err) } d.SetId(aws.StringValue(output.CertificateArn)) @@ -332,20 +333,20 @@ func resourceCertificateCreate(d *schema.ResourceData, meta interface{}) error { output, err := conn.ImportCertificate(input) if err != nil { - return fmt.Errorf("importing ACM Certificate: %w", err) + return diag.Errorf("importing ACM Certificate: %s", err) } d.SetId(aws.StringValue(output.CertificateArn)) } if _, err := waitCertificateDomainValidationsAvailable(conn, d.Id(), certificateDNSValidationAssignmentTimeout); err != nil { - return fmt.Errorf("waiting for ACM Certificate (%s) to be issued: %w", d.Id(), err) + return diag.Errorf("waiting for ACM Certificate (%s) to be issued: %s", d.Id(), err) } - return resourceCertificateRead(d, meta) + return resourceCertificateRead(ctx, d, meta) } -func resourceCertificateRead(d *schema.ResourceData, meta interface{}) error { +func resourceCertificateRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*conns.AWSClient).ACMConn defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig @@ -359,7 +360,7 @@ func resourceCertificateRead(d *schema.ResourceData, meta interface{}) error { } if err != nil { - return fmt.Errorf("reading ACM Certificate (%s): %w", d.Id(), err) + return diag.Errorf("reading ACM Certificate (%s): %s", d.Id(), err) } domainValidationOptions, validationEmails := flattenDomainValidations(certificate.DomainValidationOptions) @@ -368,21 +369,21 @@ func resourceCertificateRead(d *schema.ResourceData, meta interface{}) error { d.Set("certificate_authority_arn", certificate.CertificateAuthorityArn) d.Set("domain_name", certificate.DomainName) if err := d.Set("domain_validation_options", domainValidationOptions); err != nil { - return fmt.Errorf("error setting domain_validation_options: %w", err) - } - if certificate.NotBefore != nil { - d.Set("not_before", aws.TimeValue(certificate.NotBefore).Format(time.RFC3339)) - } else { - d.Set("not_before", nil) + return diag.Errorf("setting domain_validation_options: %s", err) } if certificate.NotAfter != nil { d.Set("not_after", aws.TimeValue(certificate.NotAfter).Format(time.RFC3339)) } else { d.Set("not_after", nil) } + if certificate.NotBefore != nil { + d.Set("not_before", aws.TimeValue(certificate.NotBefore).Format(time.RFC3339)) + } else { + d.Set("not_before", nil) + } if certificate.Options != nil { if err := d.Set("options", []interface{}{flattenCertificateOptions(certificate.Options)}); err != nil { - return fmt.Errorf("error setting options: %w", err) + return diag.Errorf("setting options: %s", err) } } else { d.Set("options", nil) @@ -395,24 +396,24 @@ func resourceCertificateRead(d *schema.ResourceData, meta interface{}) error { tags, err := ListTags(conn, d.Id()) if err != nil { - return fmt.Errorf("listing tags for ACM Certificate (%s): %w", d.Id(), err) + return diag.Errorf("listing tags for ACM Certificate (%s): %s", d.Id(), err) } tags = tags.IgnoreAWS().IgnoreConfig(ignoreTagsConfig) //lintignore:AWSR002 if err := d.Set("tags", tags.RemoveDefaultConfig(defaultTagsConfig).Map()); err != nil { - return fmt.Errorf("error setting tags: %w", err) + return diag.Errorf("setting tags: %s", err) } if err := d.Set("tags_all", tags.Map()); err != nil { - return fmt.Errorf("error setting tags_all: %w", err) + return diag.Errorf("setting tags_all: %s", err) } return nil } -func resourceCertificateUpdate(d *schema.ResourceData, meta interface{}) error { +func resourceCertificateUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*conns.AWSClient).ACMConn if d.HasChanges("private_key", "certificate_body", "certificate_chain") { @@ -436,7 +437,7 @@ func resourceCertificateUpdate(d *schema.ResourceData, meta interface{}) error { _, err := conn.ImportCertificate(input) if err != nil { - return fmt.Errorf("re-importing ACM Certificate (%s): %w", d.Id(), err) + return diag.Errorf("re-importing ACM Certificate (%s): %s", d.Id(), err) } } } @@ -445,14 +446,14 @@ func resourceCertificateUpdate(d *schema.ResourceData, meta interface{}) error { o, n := d.GetChange("tags_all") if err := UpdateTags(conn, d.Id(), o, n); err != nil { - return fmt.Errorf("error updating tags: %w", err) + return diag.Errorf("updating ACM Certificate (%s) tags: %s", d.Id(), err) } } - return resourceCertificateRead(d, meta) + return resourceCertificateRead(ctx, d, meta) } -func resourceCertificateDelete(d *schema.ResourceData, meta interface{}) error { +func resourceCertificateDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*conns.AWSClient).ACMConn log.Printf("[INFO] Deleting ACM Certificate: %s", d.Id()) @@ -468,7 +469,7 @@ func resourceCertificateDelete(d *schema.ResourceData, meta interface{}) error { } if err != nil { - return fmt.Errorf("deleting ACM Certificate (%s): %w", d.Id(), err) + return diag.Errorf("deleting ACM Certificate (%s): %w", d.Id(), err) } return nil From d8140c90489582e4459ec8a5e43837a1da062874 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 12 Sep 2022 14:27:22 -0400 Subject: [PATCH 02/26] r/aws_acm_certificate_validation: Switch to 'WithoutTimeout' CRUD handler signatures (#15090). --- .../service/acm/certificate_validation.go | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/internal/service/acm/certificate_validation.go b/internal/service/acm/certificate_validation.go index 012d9c96fda..5ff4362921f 100644 --- a/internal/service/acm/certificate_validation.go +++ b/internal/service/acm/certificate_validation.go @@ -1,6 +1,7 @@ package acm import ( + "context" "errors" "fmt" "log" @@ -10,6 +11,7 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/acm" multierror "github.com/hashicorp/go-multierror" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" @@ -18,9 +20,9 @@ import ( func ResourceCertificateValidation() *schema.Resource { return &schema.Resource{ - Create: resourceCertificateValidationCreate, - Read: resourceCertificateValidationRead, - Delete: schema.Noop, + CreateWithoutTimeout: resourceCertificateValidationCreate, + ReadWithoutTimeout: resourceCertificateValidationRead, + DeleteWithoutTimeout: schema.NoopContext, Timeouts: &schema.ResourceTimeout{ Create: schema.DefaultTimeout(75 * time.Minute), @@ -42,18 +44,18 @@ func ResourceCertificateValidation() *schema.Resource { } } -func resourceCertificateValidationCreate(d *schema.ResourceData, meta interface{}) error { +func resourceCertificateValidationCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*conns.AWSClient).ACMConn arn := d.Get("certificate_arn").(string) certificate, err := FindCertificateByARN(conn, arn) if err != nil { - return fmt.Errorf("reading ACM Certificate (%s): %w", arn, err) + return diag.Errorf("reading ACM Certificate (%s): %s", arn, err) } if v := aws.StringValue(certificate.Type); v != acm.CertificateTypeAmazonIssued { - return fmt.Errorf("ACM Certificate (%s) has type %s, no validation necessary", arn, v) + return diag.Errorf("ACM Certificate (%s) has type %s, no validation necessary", arn, v) } if v, ok := d.GetOk("validation_record_fqdns"); ok && v.(*schema.Set).Len() > 0 { @@ -61,7 +63,7 @@ func resourceCertificateValidationCreate(d *schema.ResourceData, meta interface{ for _, domainValidation := range certificate.DomainValidationOptions { if v := aws.StringValue(domainValidation.ValidationMethod); v != acm.ValidationMethodDns { - return fmt.Errorf("validation_record_fqdns is not valid for %s validation", v) + return diag.Errorf("validation_record_fqdns is not valid for %s validation", v) } if v := domainValidation.ResourceRecord; v != nil { @@ -82,20 +84,20 @@ func resourceCertificateValidationCreate(d *schema.ResourceData, meta interface{ errs = multierror.Append(errs, fmt.Errorf("missing %s DNS validation record: %s", aws.StringValue(domainValidation.DomainName), fqdn)) } - return errs + return diag.FromErr(errs) } } if _, err := waitCertificateIssued(conn, arn, d.Timeout(schema.TimeoutCreate)); err != nil { - return fmt.Errorf("waiting for ACM Certificate (%s) to be issued: %w", arn, err) + return diag.Errorf("waiting for ACM Certificate (%s) to be issued: %s", arn, err) } d.SetId(aws.TimeValue(certificate.IssuedAt).String()) - return resourceCertificateValidationRead(d, meta) + return resourceCertificateValidationRead(ctx, d, meta) } -func resourceCertificateValidationRead(d *schema.ResourceData, meta interface{}) error { +func resourceCertificateValidationRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*conns.AWSClient).ACMConn arn := d.Get("certificate_arn").(string) @@ -108,7 +110,7 @@ func resourceCertificateValidationRead(d *schema.ResourceData, meta interface{}) } if err != nil { - return fmt.Errorf("reading ACM Certificate (%s): %w", arn, err) + return diag.Errorf("reading ACM Certificate (%s): %s", arn, err) } d.Set("certificate_arn", certificate.CertificateArn) From 3fd53c03ed5a17d2fb9e8a7772dc9628ed89d63c Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 12 Sep 2022 14:30:15 -0400 Subject: [PATCH 03/26] r/aws_acm_certificate(_validation): Pass context. --- internal/service/acm/certificate.go | 36 +++++++++---------- internal/service/acm/certificate_test.go | 5 +-- .../service/acm/certificate_validation.go | 20 +++++------ .../acm/certificate_validation_test.go | 3 +- 4 files changed, 33 insertions(+), 31 deletions(-) diff --git a/internal/service/acm/certificate.go b/internal/service/acm/certificate.go index 3254d94c362..ea4268af5a7 100644 --- a/internal/service/acm/certificate.go +++ b/internal/service/acm/certificate.go @@ -309,7 +309,7 @@ func resourceCertificateCreate(ctx context.Context, d *schema.ResourceData, meta } log.Printf("[DEBUG] Requesting ACM Certificate: %s", input) - output, err := conn.RequestCertificate(input) + output, err := conn.RequestCertificateWithContext(ctx, input) if err != nil { return diag.Errorf("requesting ACM Certificate (%s): %s", domainName, err) @@ -330,7 +330,7 @@ func resourceCertificateCreate(ctx context.Context, d *schema.ResourceData, meta input.Tags = Tags(tags.IgnoreAWS()) } - output, err := conn.ImportCertificate(input) + output, err := conn.ImportCertificateWithContext(ctx, input) if err != nil { return diag.Errorf("importing ACM Certificate: %s", err) @@ -339,7 +339,7 @@ func resourceCertificateCreate(ctx context.Context, d *schema.ResourceData, meta d.SetId(aws.StringValue(output.CertificateArn)) } - if _, err := waitCertificateDomainValidationsAvailable(conn, d.Id(), certificateDNSValidationAssignmentTimeout); err != nil { + if _, err := waitCertificateDomainValidationsAvailable(ctx, conn, d.Id(), certificateDNSValidationAssignmentTimeout); err != nil { return diag.Errorf("waiting for ACM Certificate (%s) to be issued: %s", d.Id(), err) } @@ -351,7 +351,7 @@ func resourceCertificateRead(ctx context.Context, d *schema.ResourceData, meta i defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig - certificate, err := FindCertificateByARN(conn, d.Id()) + certificate, err := FindCertificateByARN(ctx, conn, d.Id()) if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] ACM Certificate %s not found, removing from state", d.Id()) @@ -393,7 +393,7 @@ func resourceCertificateRead(ctx context.Context, d *schema.ResourceData, meta i d.Set("validation_emails", validationEmails) d.Set("validation_method", certificateValidationMethod(certificate)) - tags, err := ListTags(conn, d.Id()) + tags, err := ListTagsWithContext(ctx, conn, d.Id()) if err != nil { return diag.Errorf("listing tags for ACM Certificate (%s): %s", d.Id(), err) @@ -434,7 +434,7 @@ func resourceCertificateUpdate(ctx context.Context, d *schema.ResourceData, meta input.CertificateChain = []byte(chain.(string)) } - _, err := conn.ImportCertificate(input) + _, err := conn.ImportCertificateWithContext(ctx, input) if err != nil { return diag.Errorf("re-importing ACM Certificate (%s): %s", d.Id(), err) @@ -445,7 +445,7 @@ func resourceCertificateUpdate(ctx context.Context, d *schema.ResourceData, meta if d.HasChange("tags_all") { o, n := d.GetChange("tags_all") - if err := UpdateTags(conn, d.Id(), o, n); err != nil { + if err := UpdateTagsWithContext(ctx, conn, d.Id(), o, n); err != nil { return diag.Errorf("updating ACM Certificate (%s) tags: %s", d.Id(), err) } } @@ -457,9 +457,9 @@ func resourceCertificateDelete(ctx context.Context, d *schema.ResourceData, meta conn := meta.(*conns.AWSClient).ACMConn log.Printf("[INFO] Deleting ACM Certificate: %s", d.Id()) - _, err := tfresource.RetryWhenAWSErrCodeEquals(certificateCrossServicePropagationTimeout, + _, err := tfresource.RetryWhenAWSErrCodeEqualsContext(ctx, certificateCrossServicePropagationTimeout, func() (interface{}, error) { - return conn.DeleteCertificate(&acm.DeleteCertificateInput{ + return conn.DeleteCertificateWithContext(ctx, &acm.DeleteCertificateInput{ CertificateArn: aws.String(d.Id()), }) }, acm.ErrCodeResourceInUseException) @@ -661,8 +661,8 @@ func isChangeNormalizeCertRemoval(oldRaw, newRaw interface{}) bool { return hex.EncodeToString(newCleanVal[:]) == old } -func findCertificate(conn *acm.ACM, input *acm.DescribeCertificateInput) (*acm.CertificateDetail, error) { - output, err := conn.DescribeCertificate(input) +func findCertificate(ctx context.Context, conn *acm.ACM, input *acm.DescribeCertificateInput) (*acm.CertificateDetail, error) { + output, err := conn.DescribeCertificateWithContext(ctx, input) if tfawserr.ErrCodeEquals(err, acm.ErrCodeResourceNotFoundException) { return nil, &resource.NotFoundError{ @@ -682,12 +682,12 @@ func findCertificate(conn *acm.ACM, input *acm.DescribeCertificateInput) (*acm.C return output.Certificate, nil } -func FindCertificateByARN(conn *acm.ACM, arn string) (*acm.CertificateDetail, error) { +func FindCertificateByARN(ctx context.Context, conn *acm.ACM, arn string) (*acm.CertificateDetail, error) { input := &acm.DescribeCertificateInput{ CertificateArn: aws.String(arn), } - output, err := findCertificate(conn, input) + output, err := findCertificate(ctx, conn, input) if err != nil { return nil, err @@ -703,9 +703,9 @@ func FindCertificateByARN(conn *acm.ACM, arn string) (*acm.CertificateDetail, er return output, nil } -func statusCertificateDomainValidationsAvailable(conn *acm.ACM, arn string) resource.StateRefreshFunc { +func statusCertificateDomainValidationsAvailable(ctx context.Context, conn *acm.ACM, arn string) resource.StateRefreshFunc { return func() (interface{}, string, error) { - certificate, err := FindCertificateByARN(conn, arn) + certificate, err := FindCertificateByARN(ctx, conn, arn) if tfresource.NotFound(err) { return nil, "", nil @@ -741,14 +741,14 @@ func statusCertificateDomainValidationsAvailable(conn *acm.ACM, arn string) reso } } -func waitCertificateDomainValidationsAvailable(conn *acm.ACM, arn string, timeout time.Duration) (*acm.CertificateDetail, error) { +func waitCertificateDomainValidationsAvailable(ctx context.Context, conn *acm.ACM, arn string, timeout time.Duration) (*acm.CertificateDetail, error) { stateConf := &resource.StateChangeConf{ Target: []string{strconv.FormatBool(true)}, - Refresh: statusCertificateDomainValidationsAvailable(conn, arn), + Refresh: statusCertificateDomainValidationsAvailable(ctx, conn, arn), Timeout: timeout, } - outputRaw, err := stateConf.WaitForState() + outputRaw, err := stateConf.WaitForStateContext(ctx) if output, ok := outputRaw.(*acm.CertificateDetail); ok { return output, err diff --git a/internal/service/acm/certificate_test.go b/internal/service/acm/certificate_test.go index ab4846a0c3b..0c0e096f852 100644 --- a/internal/service/acm/certificate_test.go +++ b/internal/service/acm/certificate_test.go @@ -1,6 +1,7 @@ package acm_test import ( + "context" "fmt" "regexp" "strconv" @@ -791,7 +792,7 @@ func testAccCheckCertificateExists(n string, v *acm.CertificateDetail) resource. conn := acctest.Provider.Meta().(*conns.AWSClient).ACMConn - output, err := tfacm.FindCertificateByARN(conn, rs.Primary.ID) + output, err := tfacm.FindCertificateByARN(context.Background(), conn, rs.Primary.ID) if err != nil { return err @@ -811,7 +812,7 @@ func testAccCheckCertificateDestroy(s *terraform.State) error { continue } - _, err := tfacm.FindCertificateByARN(conn, rs.Primary.ID) + _, err := tfacm.FindCertificateByARN(context.Background(), conn, rs.Primary.ID) if tfresource.NotFound(err) { continue diff --git a/internal/service/acm/certificate_validation.go b/internal/service/acm/certificate_validation.go index 5ff4362921f..c539cdb25f0 100644 --- a/internal/service/acm/certificate_validation.go +++ b/internal/service/acm/certificate_validation.go @@ -48,7 +48,7 @@ func resourceCertificateValidationCreate(ctx context.Context, d *schema.Resource conn := meta.(*conns.AWSClient).ACMConn arn := d.Get("certificate_arn").(string) - certificate, err := FindCertificateByARN(conn, arn) + certificate, err := FindCertificateByARN(ctx, conn, arn) if err != nil { return diag.Errorf("reading ACM Certificate (%s): %s", arn, err) @@ -88,7 +88,7 @@ func resourceCertificateValidationCreate(ctx context.Context, d *schema.Resource } } - if _, err := waitCertificateIssued(conn, arn, d.Timeout(schema.TimeoutCreate)); err != nil { + if _, err := waitCertificateIssued(ctx, conn, arn, d.Timeout(schema.TimeoutCreate)); err != nil { return diag.Errorf("waiting for ACM Certificate (%s) to be issued: %s", arn, err) } @@ -101,7 +101,7 @@ func resourceCertificateValidationRead(ctx context.Context, d *schema.ResourceDa conn := meta.(*conns.AWSClient).ACMConn arn := d.Get("certificate_arn").(string) - certificate, err := FindCertificateValidationByARN(conn, arn) + certificate, err := FindCertificateValidationByARN(ctx, conn, arn) if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] ACM Certificate %s not found, removing from state", arn) @@ -118,8 +118,8 @@ func resourceCertificateValidationRead(ctx context.Context, d *schema.ResourceDa return nil } -func FindCertificateValidationByARN(conn *acm.ACM, arn string) (*acm.CertificateDetail, error) { - output, err := FindCertificateByARN(conn, arn) +func FindCertificateValidationByARN(ctx context.Context, conn *acm.ACM, arn string) (*acm.CertificateDetail, error) { + output, err := FindCertificateByARN(ctx, conn, arn) if err != nil { return nil, err @@ -135,14 +135,14 @@ func FindCertificateValidationByARN(conn *acm.ACM, arn string) (*acm.Certificate return output, nil } -func statusCertificate(conn *acm.ACM, arn string) resource.StateRefreshFunc { +func statusCertificate(ctx context.Context, conn *acm.ACM, arn string) resource.StateRefreshFunc { return func() (interface{}, string, error) { // Don't call FindCertificateByARN as it maps useful status codes to NotFoundError. input := &acm.DescribeCertificateInput{ CertificateArn: aws.String(arn), } - output, err := findCertificate(conn, input) + output, err := findCertificate(ctx, conn, input) if tfresource.NotFound(err) { return nil, "", nil @@ -156,15 +156,15 @@ func statusCertificate(conn *acm.ACM, arn string) resource.StateRefreshFunc { } } -func waitCertificateIssued(conn *acm.ACM, arn string, timeout time.Duration) (*acm.CertificateDetail, error) { +func waitCertificateIssued(ctx context.Context, conn *acm.ACM, arn string, timeout time.Duration) (*acm.CertificateDetail, error) { stateConf := &resource.StateChangeConf{ Pending: []string{acm.CertificateStatusPendingValidation}, Target: []string{acm.CertificateStatusIssued}, - Refresh: statusCertificate(conn, arn), + Refresh: statusCertificate(ctx, conn, arn), Timeout: timeout, } - outputRaw, err := stateConf.WaitForState() + outputRaw, err := stateConf.WaitForStateContext(ctx) if output, ok := outputRaw.(*acm.CertificateDetail); ok { switch aws.StringValue(output.Status) { diff --git a/internal/service/acm/certificate_validation_test.go b/internal/service/acm/certificate_validation_test.go index c424e32e5f6..ccef8c1d4ae 100644 --- a/internal/service/acm/certificate_validation_test.go +++ b/internal/service/acm/certificate_validation_test.go @@ -1,6 +1,7 @@ package acm_test import ( + "context" "fmt" "regexp" "strconv" @@ -233,7 +234,7 @@ func testAccCheckCertificateValidationExists(n string) resource.TestCheckFunc { conn := acctest.Provider.Meta().(*conns.AWSClient).ACMConn - _, err := tfacm.FindCertificateValidationByARN(conn, rs.Primary.Attributes["certificate_arn"]) + _, err := tfacm.FindCertificateValidationByARN(context.Background(), conn, rs.Primary.Attributes["certificate_arn"]) return err } From aa92ea22ed829183407125e094e9d7746c602e86 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 12 Sep 2022 14:32:21 -0400 Subject: [PATCH 04/26] d/aws_acm_certificate: Alphabetize attributes. --- .../service/acm/certificate_data_source.go | 39 +++++++++---------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/internal/service/acm/certificate_data_source.go b/internal/service/acm/certificate_data_source.go index 41c39aa3da2..f97dc24e0c4 100644 --- a/internal/service/acm/certificate_data_source.go +++ b/internal/service/acm/certificate_data_source.go @@ -16,11 +16,8 @@ import ( func DataSourceCertificate() *schema.Resource { return &schema.Resource{ Read: dataSourceCertificateRead, + Schema: map[string]*schema.Schema{ - "domain": { - Type: schema.TypeString, - Required: true, - }, "arn": { Type: schema.TypeString, Computed: true, @@ -33,36 +30,38 @@ func DataSourceCertificate() *schema.Resource { Type: schema.TypeString, Computed: true, }, - "statuses": { - Type: schema.TypeList, - Optional: true, - Elem: &schema.Schema{Type: schema.TypeString}, - }, - "status": { + "domain": { Type: schema.TypeString, - Computed: true, + Required: true, }, "key_types": { Type: schema.TypeSet, Optional: true, Elem: &schema.Schema{ - Type: schema.TypeString, - ValidateFunc: validation.StringInSlice( - acm.KeyAlgorithm_Values(), false, - ), + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice(acm.KeyAlgorithm_Values(), false), }, }, - "types": { - Type: schema.TypeList, - Optional: true, - Elem: &schema.Schema{Type: schema.TypeString}, - }, "most_recent": { Type: schema.TypeBool, Optional: true, Default: false, }, + "status": { + Type: schema.TypeString, + Computed: true, + }, + "statuses": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, "tags": tftags.TagsSchemaComputed(), + "types": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, }, } } From 73c15cfb3d18256219e5ad1c2861570101c5532a Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 12 Sep 2022 14:39:20 -0400 Subject: [PATCH 05/26] d/aws_acm_certificate: Switch to 'WithoutTimeout' CRUD handler signatures (#15090). --- .../service/acm/certificate_data_source.go | 57 ++++++++++--------- 1 file changed, 31 insertions(+), 26 deletions(-) diff --git a/internal/service/acm/certificate_data_source.go b/internal/service/acm/certificate_data_source.go index f97dc24e0c4..713f0ec4fd2 100644 --- a/internal/service/acm/certificate_data_source.go +++ b/internal/service/acm/certificate_data_source.go @@ -1,11 +1,13 @@ package acm import ( + "context" "fmt" "log" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/acm" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" @@ -15,7 +17,7 @@ import ( func DataSourceCertificate() *schema.Resource { return &schema.Resource{ - Read: dataSourceCertificateRead, + ReadWithoutTimeout: dataSourceCertificateRead, Schema: map[string]*schema.Schema{ "arn": { @@ -66,7 +68,7 @@ func DataSourceCertificate() *schema.Resource { } } -func dataSourceCertificateRead(d *schema.ResourceData, meta interface{}) error { +func dataSourceCertificateRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*conns.AWSClient).ACMConn ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig @@ -89,7 +91,7 @@ func dataSourceCertificateRead(d *schema.ResourceData, meta interface{}) error { var arns []*string log.Printf("[DEBUG] Reading ACM Certificate: %s", params) - err := conn.ListCertificatesPages(params, func(page *acm.ListCertificatesOutput, lastPage bool) bool { + err := conn.ListCertificatesPagesWithContext(ctx, params, func(page *acm.ListCertificatesOutput, lastPage bool) bool { for _, cert := range page.CertificateSummaryList { if aws.StringValue(cert.DomainName) == target { arns = append(arns, cert.CertificateArn) @@ -98,12 +100,13 @@ func dataSourceCertificateRead(d *schema.ResourceData, meta interface{}) error { return true }) + if err != nil { - return fmt.Errorf("listing certificates: %w", err) + return diag.Errorf("listing ACM Certificates: %s", err) } if len(arns) == 0 { - return fmt.Errorf("No certificate for domain %q found in this region", target) + return diag.Errorf("no certificate for domain %q found in this Region", target) } filterMostRecent := d.Get("most_recent").(bool) @@ -113,22 +116,21 @@ func dataSourceCertificateRead(d *schema.ResourceData, meta interface{}) error { if !filterMostRecent && !filterTypesOk && len(arns) > 1 { // Multiple certificates have been found and no additional filtering set - return fmt.Errorf("Multiple certificates for domain %q found in this region", target) + return diag.Errorf("multiple certificates for domain %q found in this Region", target) } typesStrings := flex.ExpandStringList(filterTypes.([]interface{})) for _, arn := range arns { - var err error + arn := aws.StringValue(arn) + output, err := conn.DescribeCertificateWithContext(ctx, &acm.DescribeCertificateInput{ + CertificateArn: aws.String(arn), + }) - input := &acm.DescribeCertificateInput{ - CertificateArn: aws.String(*arn), - } - log.Printf("[DEBUG] Describing ACM Certificate: %s", input) - output, err := conn.DescribeCertificate(input) if err != nil { - return fmt.Errorf("describing ACM certificate: %w", err) + return diag.Errorf("reading ACM Certificate (%s): %s", arn, err) } + certificate := output.Certificate if filterTypesOk { @@ -144,12 +146,12 @@ func dataSourceCertificateRead(d *schema.ResourceData, meta interface{}) error { if filterMostRecent { matchedCertificate, err = mostRecentCertificate(certificate, matchedCertificate) if err != nil { - return err + return diag.FromErr(err) } break } // Now we have multiple candidate certificates and we only allow one certificate - return fmt.Errorf("Multiple certificates for domain %q found in this region", target) + return diag.Errorf("multiple certificates for domain %q found in this Region", target) } } continue @@ -164,27 +166,28 @@ func dataSourceCertificateRead(d *schema.ResourceData, meta interface{}) error { if filterMostRecent { matchedCertificate, err = mostRecentCertificate(certificate, matchedCertificate) if err != nil { - return err + return diag.FromErr(err) } continue } // Now we have multiple candidate certificates and we only allow one certificate - return fmt.Errorf("Multiple certificates for domain %q found in this region", target) + return diag.Errorf("multiple certificates for domain %q found in this Region", target) } if matchedCertificate == nil { - return fmt.Errorf("No certificate for domain %q found in this region", target) + return diag.Errorf("no certificate for domain %q found in this Region", target) } // Get the certificate data if the status is issued var certOutput *acm.GetCertificateOutput if aws.StringValue(matchedCertificate.Status) == acm.CertificateStatusIssued { - getCertInput := acm.GetCertificateInput{ - CertificateArn: matchedCertificate.CertificateArn, - } - certOutput, err = conn.GetCertificate(&getCertInput) + arn := aws.StringValue(matchedCertificate.CertificateArn) + certOutput, err = conn.GetCertificateWithContext(ctx, &acm.GetCertificateInput{ + CertificateArn: aws.String(arn), + }) + if err != nil { - return fmt.Errorf("error getting ACM certificate (%s): %w", aws.StringValue(matchedCertificate.CertificateArn), err) + return diag.Errorf("reading ACM Certificate (%s): %s", arn, err) } } if certOutput != nil { @@ -198,13 +201,15 @@ func dataSourceCertificateRead(d *schema.ResourceData, meta interface{}) error { d.SetId(aws.StringValue(matchedCertificate.CertificateArn)) d.Set("arn", matchedCertificate.CertificateArn) d.Set("status", matchedCertificate.Status) - tags, err := ListTags(conn, aws.StringValue(matchedCertificate.CertificateArn)) + + tags, err := ListTagsWithContext(ctx, conn, aws.StringValue(matchedCertificate.CertificateArn)) + if err != nil { - return fmt.Errorf("error listing tags for ACM Certificate (%s): %w", d.Id(), err) + return diag.Errorf("listing tags for ACM Certificate (%s): %s", d.Id(), err) } if err := d.Set("tags", tags.IgnoreAWS().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { - return fmt.Errorf("error setting tags: %w", err) + return diag.Errorf("setting tags: %s", err) } return nil From d39409e46f0f8b7ee3640b73321c2768491ca3f5 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 12 Sep 2022 17:11:18 -0400 Subject: [PATCH 06/26] Fix 'github.com/hashicorp/terraform-plugin-sdk/v2/diag.Errorf does not support error-wrapping directive %w'. --- internal/service/acm/certificate.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/service/acm/certificate.go b/internal/service/acm/certificate.go index ea4268af5a7..ab8b336c11d 100644 --- a/internal/service/acm/certificate.go +++ b/internal/service/acm/certificate.go @@ -469,7 +469,7 @@ func resourceCertificateDelete(ctx context.Context, d *schema.ResourceData, meta } if err != nil { - return diag.Errorf("deleting ACM Certificate (%s): %w", d.Id(), err) + return diag.Errorf("deleting ACM Certificate (%s): %s", d.Id(), err) } return nil From 3f407e90a70c70fb3fcdd44d5430e52229054d2d Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 12 Sep 2022 18:06:44 -0400 Subject: [PATCH 07/26] d/aws_acm_certificate: Tweaked error message. --- .../acm/certificate_data_source_test.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/internal/service/acm/certificate_data_source_test.go b/internal/service/acm/certificate_data_source_test.go index c5d9f82edea..6e4355191cf 100644 --- a/internal/service/acm/certificate_data_source_test.go +++ b/internal/service/acm/certificate_data_source_test.go @@ -130,15 +130,15 @@ func TestAccACMCertificateDataSource_multipleIssued(t *testing.T) { Steps: []resource.TestStep{ { Config: testAccCertificateDataSourceConfig_basic(domain), - ExpectError: regexp.MustCompile(`Multiple certificates for domain`), + ExpectError: regexp.MustCompile(`multiple certificates for domain`), }, { Config: testAccCertificateDataSourceConfig_status(domain, acm.CertificateStatusIssued), - ExpectError: regexp.MustCompile(`Multiple certificates for domain`), + ExpectError: regexp.MustCompile(`multiple certificates for domain`), }, { Config: testAccCertificateDataSourceConfig_types(domain, acm.CertificateTypeAmazonIssued), - ExpectError: regexp.MustCompile(`Multiple certificates for domain`), + ExpectError: regexp.MustCompile(`multiple certificates for domain`), }, { Config: testAccCertificateDataSourceConfig_mostRecent(domain, true), @@ -179,27 +179,27 @@ func TestAccACMCertificateDataSource_noMatchReturnsError(t *testing.T) { Steps: []resource.TestStep{ { Config: testAccCertificateDataSourceConfig_basic(domain), - ExpectError: regexp.MustCompile(`No certificate for domain`), + ExpectError: regexp.MustCompile(`no certificate for domain`), }, { Config: testAccCertificateDataSourceConfig_status(domain, acm.CertificateStatusIssued), - ExpectError: regexp.MustCompile(`No certificate for domain`), + ExpectError: regexp.MustCompile(`no certificate for domain`), }, { Config: testAccCertificateDataSourceConfig_types(domain, acm.CertificateTypeAmazonIssued), - ExpectError: regexp.MustCompile(`No certificate for domain`), + ExpectError: regexp.MustCompile(`no certificate for domain`), }, { Config: testAccCertificateDataSourceConfig_mostRecent(domain, true), - ExpectError: regexp.MustCompile(`No certificate for domain`), + ExpectError: regexp.MustCompile(`no certificate for domain`), }, { Config: testAccCertificateDataSourceConfig_mostRecentAndStatus(domain, acm.CertificateStatusIssued, true), - ExpectError: regexp.MustCompile(`No certificate for domain`), + ExpectError: regexp.MustCompile(`no certificate for domain`), }, { Config: testAccCertificateDataSourceConfig_mostRecentAndTypes(domain, acm.CertificateTypeAmazonIssued, true), - ExpectError: regexp.MustCompile(`No certificate for domain`), + ExpectError: regexp.MustCompile(`no certificate for domain`), }, }, }) From c39bfecabc8a15c6d0cf90d155daa0a8ccff4ab4 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 12 Sep 2022 18:07:06 -0400 Subject: [PATCH 08/26] % ACM_CERTIFICATE_ROOT_DOMAIN=ewbankkit.com make testacc TESTARGS='-run=TestAccACMCertificate_' PKG=acm ACCTEST_PARALLELISM=1 ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./internal/service/acm/... -v -count 1 -parallel 1 -run=TestAccACMCertificate_ -timeout 180m === RUN TestAccACMCertificate_emailValidation === PAUSE TestAccACMCertificate_emailValidation === RUN TestAccACMCertificate_dnsValidation === PAUSE TestAccACMCertificate_dnsValidation === RUN TestAccACMCertificate_root === PAUSE TestAccACMCertificate_root === RUN TestAccACMCertificate_validationOptions === PAUSE TestAccACMCertificate_validationOptions === RUN TestAccACMCertificate_privateCert === PAUSE TestAccACMCertificate_privateCert === RUN TestAccACMCertificate_Root_trailingPeriod === PAUSE TestAccACMCertificate_Root_trailingPeriod === RUN TestAccACMCertificate_rootAndWildcardSan === PAUSE TestAccACMCertificate_rootAndWildcardSan === RUN TestAccACMCertificate_SubjectAlternativeNames_emptyString === PAUSE TestAccACMCertificate_SubjectAlternativeNames_emptyString === RUN TestAccACMCertificate_San_single === PAUSE TestAccACMCertificate_San_single === RUN TestAccACMCertificate_San_multiple === PAUSE TestAccACMCertificate_San_multiple === RUN TestAccACMCertificate_San_trailingPeriod === PAUSE TestAccACMCertificate_San_trailingPeriod === RUN TestAccACMCertificate_San_matches_domain === PAUSE TestAccACMCertificate_San_matches_domain === RUN TestAccACMCertificate_wildcard === PAUSE TestAccACMCertificate_wildcard === RUN TestAccACMCertificate_wildcardAndRootSan === PAUSE TestAccACMCertificate_wildcardAndRootSan === RUN TestAccACMCertificate_disableCTLogging === PAUSE TestAccACMCertificate_disableCTLogging === RUN TestAccACMCertificate_Imported_domainName === PAUSE TestAccACMCertificate_Imported_domainName === RUN TestAccACMCertificate_Imported_validityDates === PAUSE TestAccACMCertificate_Imported_validityDates === RUN TestAccACMCertificate_Imported_ipAddress === PAUSE TestAccACMCertificate_Imported_ipAddress === RUN TestAccACMCertificate_PrivateKey_tags === PAUSE TestAccACMCertificate_PrivateKey_tags === CONT TestAccACMCertificate_emailValidation --- PASS: TestAccACMCertificate_emailValidation (28.62s) === CONT TestAccACMCertificate_San_trailingPeriod --- PASS: TestAccACMCertificate_San_trailingPeriod (25.70s) === CONT TestAccACMCertificate_PrivateKey_tags --- PASS: TestAccACMCertificate_PrivateKey_tags (52.14s) === CONT TestAccACMCertificate_Imported_ipAddress --- PASS: TestAccACMCertificate_Imported_ipAddress (17.16s) === CONT TestAccACMCertificate_Imported_validityDates --- PASS: TestAccACMCertificate_Imported_validityDates (21.23s) === CONT TestAccACMCertificate_Imported_domainName --- PASS: TestAccACMCertificate_Imported_domainName (41.05s) === CONT TestAccACMCertificate_disableCTLogging --- PASS: TestAccACMCertificate_disableCTLogging (24.55s) === CONT TestAccACMCertificate_wildcardAndRootSan --- PASS: TestAccACMCertificate_wildcardAndRootSan (22.70s) === CONT TestAccACMCertificate_wildcard --- PASS: TestAccACMCertificate_wildcard (22.48s) === CONT TestAccACMCertificate_San_matches_domain --- PASS: TestAccACMCertificate_San_matches_domain (31.43s) === CONT TestAccACMCertificate_Root_trailingPeriod --- PASS: TestAccACMCertificate_Root_trailingPeriod (1.08s) === CONT TestAccACMCertificate_San_multiple --- PASS: TestAccACMCertificate_San_multiple (29.76s) === CONT TestAccACMCertificate_San_single --- PASS: TestAccACMCertificate_San_single (27.45s) === CONT TestAccACMCertificate_SubjectAlternativeNames_emptyString --- PASS: TestAccACMCertificate_SubjectAlternativeNames_emptyString (1.03s) === CONT TestAccACMCertificate_rootAndWildcardSan --- PASS: TestAccACMCertificate_rootAndWildcardSan (23.83s) === CONT TestAccACMCertificate_validationOptions --- PASS: TestAccACMCertificate_validationOptions (25.89s) === CONT TestAccACMCertificate_privateCert --- PASS: TestAccACMCertificate_privateCert (23.22s) === CONT TestAccACMCertificate_root --- PASS: TestAccACMCertificate_root (24.48s) === CONT TestAccACMCertificate_dnsValidation --- PASS: TestAccACMCertificate_dnsValidation (26.30s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/acm 474.217s % ACM_CERTIFICATE_ROOT_DOMAIN=ewbankkit.com make testacc TESTARGS='-run=TestAccACMCertificateValidation_' PKG=acm ACCTEST_PARALLELISM=1 ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./internal/service/acm/... -v -count 1 -parallel 1 -run=TestAccACMCertificateValidation_ -timeout 180m === RUN TestAccACMCertificateValidation_basic === PAUSE TestAccACMCertificateValidation_basic === RUN TestAccACMCertificateValidation_timeout === PAUSE TestAccACMCertificateValidation_timeout === RUN TestAccACMCertificateValidation_validationRecordFQDNS === PAUSE TestAccACMCertificateValidation_validationRecordFQDNS === RUN TestAccACMCertificateValidation_validationRecordFQDNSEmail === PAUSE TestAccACMCertificateValidation_validationRecordFQDNSEmail === RUN TestAccACMCertificateValidation_validationRecordFQDNSRoot === PAUSE TestAccACMCertificateValidation_validationRecordFQDNSRoot === RUN TestAccACMCertificateValidation_validationRecordFQDNSRootAndWildcard === PAUSE TestAccACMCertificateValidation_validationRecordFQDNSRootAndWildcard === RUN TestAccACMCertificateValidation_validationRecordFQDNSSan === PAUSE TestAccACMCertificateValidation_validationRecordFQDNSSan === RUN TestAccACMCertificateValidation_validationRecordFQDNSWildcard === PAUSE TestAccACMCertificateValidation_validationRecordFQDNSWildcard === RUN TestAccACMCertificateValidation_validationRecordFQDNSWildcardAndRoot === PAUSE TestAccACMCertificateValidation_validationRecordFQDNSWildcardAndRoot === CONT TestAccACMCertificateValidation_basic --- PASS: TestAccACMCertificateValidation_basic (109.86s) === CONT TestAccACMCertificateValidation_validationRecordFQDNSRootAndWildcard --- PASS: TestAccACMCertificateValidation_validationRecordFQDNSRootAndWildcard (106.91s) === CONT TestAccACMCertificateValidation_validationRecordFQDNSWildcardAndRoot --- PASS: TestAccACMCertificateValidation_validationRecordFQDNSWildcardAndRoot (93.59s) === CONT TestAccACMCertificateValidation_validationRecordFQDNSWildcard --- PASS: TestAccACMCertificateValidation_validationRecordFQDNSWildcard (101.64s) === CONT TestAccACMCertificateValidation_validationRecordFQDNSSan --- PASS: TestAccACMCertificateValidation_validationRecordFQDNSSan (116.42s) === CONT TestAccACMCertificateValidation_validationRecordFQDNSEmail --- PASS: TestAccACMCertificateValidation_validationRecordFQDNSEmail (17.50s) === CONT TestAccACMCertificateValidation_validationRecordFQDNSRoot --- PASS: TestAccACMCertificateValidation_validationRecordFQDNSRoot (200.50s) === CONT TestAccACMCertificateValidation_validationRecordFQDNS --- PASS: TestAccACMCertificateValidation_validationRecordFQDNS (102.31s) === CONT TestAccACMCertificateValidation_timeout --- PASS: TestAccACMCertificateValidation_timeout (19.66s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/acm 872.367s From 6c56cc7e3c8ba8f66febda435865f8d8135c8d7f Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 13 Sep 2022 08:52:32 -0400 Subject: [PATCH 09/26] r/aws_acm_certificate: Add 'type' attribute. Acceptance test output: % ACM_CERTIFICATE_ROOT_DOMAIN=ewbankkit.com make testacc TESTARGS='-run=TestAccACMCertificate_Imported_validityDates\|TestAccACMCertificate_privateCert\|TestAccACMCertificate_emailValidation\|TestAccACMCertificate_dnsValidation' PKG=acm ACCTEST_PARALLELISM=1 ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./internal/service/acm/... -v -count 1 -parallel 1 -run=TestAccACMCertificate_Imported_validityDates\|TestAccACMCertificate_privateCert\|TestAccACMCertificate_emailValidation\|TestAccACMCertificate_dnsValidation -timeout 180m === RUN TestAccACMCertificate_emailValidation === PAUSE TestAccACMCertificate_emailValidation === RUN TestAccACMCertificate_dnsValidation === PAUSE TestAccACMCertificate_dnsValidation === RUN TestAccACMCertificate_privateCert === PAUSE TestAccACMCertificate_privateCert === RUN TestAccACMCertificate_Imported_validityDates === PAUSE TestAccACMCertificate_Imported_validityDates === CONT TestAccACMCertificate_emailValidation --- PASS: TestAccACMCertificate_emailValidation (48.62s) === CONT TestAccACMCertificate_privateCert --- PASS: TestAccACMCertificate_privateCert (25.11s) === CONT TestAccACMCertificate_Imported_validityDates --- PASS: TestAccACMCertificate_Imported_validityDates (17.22s) === CONT TestAccACMCertificate_dnsValidation --- PASS: TestAccACMCertificate_dnsValidation (23.12s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/acm 118.083s --- .changelog/######.txt | 3 +++ internal/service/acm/certificate.go | 5 +++++ internal/service/acm/certificate_test.go | 12 ++++++++---- website/docs/r/acm_certificate.html.markdown | 1 + 4 files changed, 17 insertions(+), 4 deletions(-) create mode 100644 .changelog/######.txt diff --git a/.changelog/######.txt b/.changelog/######.txt new file mode 100644 index 00000000000..3638c4950ea --- /dev/null +++ b/.changelog/######.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +resource/aws_acm_certificate: Add `type` attribute +``` \ No newline at end of file diff --git a/internal/service/acm/certificate.go b/internal/service/acm/certificate.go index ab8b336c11d..a8bdda58d16 100644 --- a/internal/service/acm/certificate.go +++ b/internal/service/acm/certificate.go @@ -173,6 +173,10 @@ func ResourceCertificate() *schema.Resource { }, "tags": tftags.TagsSchema(), "tags_all": tftags.TagsSchemaComputed(), + "type": { + Type: schema.TypeString, + Computed: true, + }, "validation_emails": { Type: schema.TypeList, Computed: true, @@ -390,6 +394,7 @@ func resourceCertificateRead(ctx context.Context, d *schema.ResourceData, meta i } d.Set("status", certificate.Status) d.Set("subject_alternative_names", aws.StringValueSlice(certificate.SubjectAlternativeNames)) + d.Set("type", certificate.Type) d.Set("validation_emails", validationEmails) d.Set("validation_method", certificateValidationMethod(certificate)) diff --git a/internal/service/acm/certificate_test.go b/internal/service/acm/certificate_test.go index 0c0e096f852..e046360ef02 100644 --- a/internal/service/acm/certificate_test.go +++ b/internal/service/acm/certificate_test.go @@ -39,6 +39,7 @@ func TestAccACMCertificate_emailValidation(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "status", acm.CertificateStatusPendingValidation), resource.TestCheckResourceAttr(resourceName, "subject_alternative_names.#", "1"), resource.TestCheckTypeSetElemAttr(resourceName, "subject_alternative_names.*", domain), + resource.TestCheckResourceAttr(resourceName, "type", "AMAZON_ISSUED"), acctest.CheckResourceAttrGreaterThanValue(resourceName, "validation_emails.#", "0"), resource.TestMatchResourceAttr(resourceName, "validation_emails.0", regexp.MustCompile(`^[^@]+@.+$`)), resource.TestCheckResourceAttr(resourceName, "validation_method", acm.ValidationMethodEmail), @@ -80,6 +81,7 @@ func TestAccACMCertificate_dnsValidation(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "status", acm.CertificateStatusPendingValidation), resource.TestCheckResourceAttr(resourceName, "subject_alternative_names.#", "1"), resource.TestCheckTypeSetElemAttr(resourceName, "subject_alternative_names.*", domain), + resource.TestCheckResourceAttr(resourceName, "type", "AMAZON_ISSUED"), resource.TestCheckResourceAttr(resourceName, "validation_emails.#", "0"), resource.TestCheckResourceAttr(resourceName, "validation_method", acm.ValidationMethodDns), resource.TestCheckResourceAttr(resourceName, "validation_option.#", "0"), @@ -189,17 +191,18 @@ func TestAccACMCertificate_privateCert(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckCertificateExists(resourceName, &v), acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "acm", regexp.MustCompile("certificate/.+$")), + resource.TestCheckResourceAttrPair(resourceName, "certificate_authority_arn", certificateAuthorityResourceName, "arn"), resource.TestCheckResourceAttr(resourceName, "domain_name", certificateDomainName), resource.TestCheckResourceAttr(resourceName, "domain_validation_options.#", "0"), + resource.TestCheckResourceAttr(resourceName, "not_after", ""), + resource.TestCheckResourceAttr(resourceName, "not_before", ""), resource.TestCheckResourceAttr(resourceName, "status", acm.CertificateStatusFailed), // FailureReason: PCA_INVALID_STATE (PCA State: PENDING_CERTIFICATE) resource.TestCheckResourceAttr(resourceName, "subject_alternative_names.#", "1"), resource.TestCheckTypeSetElemAttr(resourceName, "subject_alternative_names.*", certificateDomainName), + resource.TestCheckResourceAttr(resourceName, "type", "PRIVATE"), 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"), - resource.TestCheckResourceAttr(resourceName, "not_before", ""), - resource.TestCheckResourceAttr(resourceName, "not_after", ""), ), }, { @@ -678,8 +681,9 @@ func TestAccACMCertificate_Imported_validityDates(t *testing.T) { Config: testAccCertificateConfig_privateKey(certificate, key, caCertificate), Check: resource.ComposeTestCheckFunc( testAccCheckCertificateExists(resourceName, &v), - acctest.CheckResourceAttrRFC3339(resourceName, "not_before"), acctest.CheckResourceAttrRFC3339(resourceName, "not_after"), + acctest.CheckResourceAttrRFC3339(resourceName, "not_before"), + resource.TestCheckResourceAttr(resourceName, "type", "IMPORTED"), ), }, { diff --git a/website/docs/r/acm_certificate.html.markdown b/website/docs/r/acm_certificate.html.markdown index d5ec9e40e50..feebf282b48 100644 --- a/website/docs/r/acm_certificate.html.markdown +++ b/website/docs/r/acm_certificate.html.markdown @@ -157,6 +157,7 @@ In addition to all arguments above, the following attributes are exported: * `not_after` - Expiration date and time of the certificate. * `not_before` - Start of the validity period of the certificate. * `status` - Status of the certificate. +* `type` - Source of the certificate. * `tags_all` - Map of tags assigned to the resource, including those inherited from the provider [`default_tags` configuration block](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#default_tags-configuration-block). * `validation_emails` - List of addresses that received a validation E-Mail. Only set if `EMAIL`-validation was used. From a0a0f6073e6fd1e519ec723c69ec8a18788e2fa4 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 13 Sep 2022 08:53:40 -0400 Subject: [PATCH 10/26] Correct CHANGELOG entry file name. --- .changelog/{######.txt => 26784.txt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .changelog/{######.txt => 26784.txt} (100%) diff --git a/.changelog/######.txt b/.changelog/26784.txt similarity index 100% rename from .changelog/######.txt rename to .changelog/26784.txt From c2a9cfe4d9f225b47612eca6019726bc39970440 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 13 Sep 2022 11:14:12 -0400 Subject: [PATCH 11/26] r/aws_acm_certificate: Add 'renewal_eligibility' attribute. Acceptance test output: % ACM_CERTIFICATE_ROOT_DOMAIN=ewbankkit.com make testacc TESTARGS='-run=TestAccACMCertificate_Imported_validityDates\|TestAccACMCertificate_privateCert' PKG=acm ACCTEST_PARALLELISM=1 ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./internal/service/acm/... -v -count 1 -parallel 1 -run=TestAccACMCertificate_Imported_validityDates\|TestAccACMCertificate_privateCert -timeout 180m === RUN TestAccACMCertificate_privateCert === PAUSE TestAccACMCertificate_privateCert === RUN TestAccACMCertificate_Imported_validityDates === PAUSE TestAccACMCertificate_Imported_validityDates === CONT TestAccACMCertificate_privateCert --- PASS: TestAccACMCertificate_privateCert (25.00s) === CONT TestAccACMCertificate_Imported_validityDates --- PASS: TestAccACMCertificate_Imported_validityDates (17.67s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/acm 47.285s --- .changelog/26784.txt | 2 +- internal/service/acm/certificate.go | 5 +++++ internal/service/acm/certificate_test.go | 2 ++ website/docs/r/acm_certificate.html.markdown | 1 + 4 files changed, 9 insertions(+), 1 deletion(-) diff --git a/.changelog/26784.txt b/.changelog/26784.txt index 3638c4950ea..c86890c6a18 100644 --- a/.changelog/26784.txt +++ b/.changelog/26784.txt @@ -1,3 +1,3 @@ ```release-note:enhancement -resource/aws_acm_certificate: Add `type` attribute +resource/aws_acm_certificate: Add `renewal_eligibility` and `type` attributes ``` \ No newline at end of file diff --git a/internal/service/acm/certificate.go b/internal/service/acm/certificate.go index a8bdda58d16..0222ea1c59f 100644 --- a/internal/service/acm/certificate.go +++ b/internal/service/acm/certificate.go @@ -150,6 +150,10 @@ func ResourceCertificate() *schema.Resource { Sensitive: true, ExactlyOneOf: []string{"domain_name", "private_key"}, }, + "renewal_eligibility": { + Type: schema.TypeString, + Computed: true, + }, "status": { Type: schema.TypeString, Computed: true, @@ -392,6 +396,7 @@ func resourceCertificateRead(ctx context.Context, d *schema.ResourceData, meta i } else { d.Set("options", nil) } + d.Set("renewal_eligibility", certificate.RenewalEligibility) d.Set("status", certificate.Status) d.Set("subject_alternative_names", aws.StringValueSlice(certificate.SubjectAlternativeNames)) d.Set("type", certificate.Type) diff --git a/internal/service/acm/certificate_test.go b/internal/service/acm/certificate_test.go index e046360ef02..012045f601a 100644 --- a/internal/service/acm/certificate_test.go +++ b/internal/service/acm/certificate_test.go @@ -196,6 +196,7 @@ func TestAccACMCertificate_privateCert(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "domain_validation_options.#", "0"), resource.TestCheckResourceAttr(resourceName, "not_after", ""), resource.TestCheckResourceAttr(resourceName, "not_before", ""), + resource.TestCheckResourceAttr(resourceName, "renewal_eligibility", "INELIGIBLE"), resource.TestCheckResourceAttr(resourceName, "status", acm.CertificateStatusFailed), // FailureReason: PCA_INVALID_STATE (PCA State: PENDING_CERTIFICATE) resource.TestCheckResourceAttr(resourceName, "subject_alternative_names.#", "1"), resource.TestCheckTypeSetElemAttr(resourceName, "subject_alternative_names.*", certificateDomainName), @@ -683,6 +684,7 @@ func TestAccACMCertificate_Imported_validityDates(t *testing.T) { testAccCheckCertificateExists(resourceName, &v), acctest.CheckResourceAttrRFC3339(resourceName, "not_after"), acctest.CheckResourceAttrRFC3339(resourceName, "not_before"), + resource.TestCheckResourceAttr(resourceName, "renewal_eligibility", "INELIGIBLE"), resource.TestCheckResourceAttr(resourceName, "type", "IMPORTED"), ), }, diff --git a/website/docs/r/acm_certificate.html.markdown b/website/docs/r/acm_certificate.html.markdown index feebf282b48..d82e6c1bc54 100644 --- a/website/docs/r/acm_certificate.html.markdown +++ b/website/docs/r/acm_certificate.html.markdown @@ -156,6 +156,7 @@ In addition to all arguments above, the following attributes are exported: * `domain_validation_options` - Set of domain validation objects which can be used to complete certificate validation. Can have more than one element, e.g., if SANs are defined. Only set if `DNS`-validation was used. * `not_after` - Expiration date and time of the certificate. * `not_before` - Start of the validity period of the certificate. +* `renewal_eligibility` - Whether the certificate is eligible for renewal. * `status` - Status of the certificate. * `type` - Source of the certificate. * `tags_all` - Map of tags assigned to the resource, including those inherited from the provider [`default_tags` configuration block](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#default_tags-configuration-block). From 082143af38a53e1a5c600f16aae3ebc9e9cc9649 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 13 Sep 2022 13:52:17 -0400 Subject: [PATCH 12/26] r/aws_acm_certificate: Add 'renewal_summary' attribute. Acceptance test output: % ACM_CERTIFICATE_ROOT_DOMAIN=ewbankkit.com make testacc TESTARGS='-run=TestAccACMCertificate_Imported_validityDates\|TestAccACMCertificate_privateCert' PKG=acm ACCTEST_PARALLELISM=1 ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./internal/service/acm/... -v -count 1 -parallel 1 -run=TestAccACMCertificate_Imported_validityDates\|TestAccACMCertificate_privateCert -timeout 180m === RUN TestAccACMCertificate_privateCert === PAUSE TestAccACMCertificate_privateCert === RUN TestAccACMCertificate_Imported_validityDates === PAUSE TestAccACMCertificate_Imported_validityDates === CONT TestAccACMCertificate_privateCert --- PASS: TestAccACMCertificate_privateCert (25.11s) === CONT TestAccACMCertificate_Imported_validityDates --- PASS: TestAccACMCertificate_Imported_validityDates (16.91s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/acm 46.751s --- .changelog/26784.txt | 2 +- internal/service/acm/certificate.go | 41 ++++++++++++++++++++ internal/service/acm/certificate_test.go | 2 + website/docs/r/acm_certificate.html.markdown | 6 +++ 4 files changed, 50 insertions(+), 1 deletion(-) diff --git a/.changelog/26784.txt b/.changelog/26784.txt index c86890c6a18..ab7e81e63a6 100644 --- a/.changelog/26784.txt +++ b/.changelog/26784.txt @@ -1,3 +1,3 @@ ```release-note:enhancement -resource/aws_acm_certificate: Add `renewal_eligibility` and `type` attributes +resource/aws_acm_certificate: Add `renewal_eligibility`, `renewal_summary` and `type` attributes ``` \ No newline at end of file diff --git a/internal/service/acm/certificate.go b/internal/service/acm/certificate.go index 0222ea1c59f..d219c2c705c 100644 --- a/internal/service/acm/certificate.go +++ b/internal/service/acm/certificate.go @@ -154,6 +154,22 @@ func ResourceCertificate() *schema.Resource { Type: schema.TypeString, Computed: true, }, + "renewal_summary": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "renewal_status": { + Type: schema.TypeString, + Computed: true, + }, + "renewal_status_reason": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, "status": { Type: schema.TypeString, Computed: true, @@ -397,6 +413,13 @@ func resourceCertificateRead(ctx context.Context, d *schema.ResourceData, meta i d.Set("options", nil) } d.Set("renewal_eligibility", certificate.RenewalEligibility) + if certificate.RenewalSummary != nil { + if err := d.Set("renewal_summary", []interface{}{flattenRenewalSummary(certificate.RenewalSummary)}); err != nil { + return diag.Errorf("setting renewal_summary: %s", err) + } + } else { + d.Set("renewal_summary", nil) + } d.Set("status", certificate.Status) d.Set("subject_alternative_names", aws.StringValueSlice(certificate.SubjectAlternativeNames)) d.Set("type", certificate.Type) @@ -640,6 +663,24 @@ func flattenDomainValidations(apiObjects []*acm.DomainValidation) ([]interface{} return tfList, tfStrings } +func flattenRenewalSummary(apiObject *acm.RenewalSummary) map[string]interface{} { + if apiObject == nil { + return nil + } + + tfMap := map[string]interface{}{} + + if v := apiObject.RenewalStatus; v != nil { + tfMap["renewal_status"] = aws.StringValue(v) + } + + if v := apiObject.RenewalStatusReason; v != nil { + tfMap["renewal_status_reason"] = aws.StringValue(v) + } + + return tfMap +} + func isChangeNormalizeCertRemoval(oldRaw, newRaw interface{}) bool { old, ok := oldRaw.(string) diff --git a/internal/service/acm/certificate_test.go b/internal/service/acm/certificate_test.go index 012045f601a..b7a2b76d55e 100644 --- a/internal/service/acm/certificate_test.go +++ b/internal/service/acm/certificate_test.go @@ -197,6 +197,7 @@ func TestAccACMCertificate_privateCert(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "not_after", ""), resource.TestCheckResourceAttr(resourceName, "not_before", ""), resource.TestCheckResourceAttr(resourceName, "renewal_eligibility", "INELIGIBLE"), + resource.TestCheckResourceAttr(resourceName, "renewal_summary.#", "0"), resource.TestCheckResourceAttr(resourceName, "status", acm.CertificateStatusFailed), // FailureReason: PCA_INVALID_STATE (PCA State: PENDING_CERTIFICATE) resource.TestCheckResourceAttr(resourceName, "subject_alternative_names.#", "1"), resource.TestCheckTypeSetElemAttr(resourceName, "subject_alternative_names.*", certificateDomainName), @@ -685,6 +686,7 @@ func TestAccACMCertificate_Imported_validityDates(t *testing.T) { acctest.CheckResourceAttrRFC3339(resourceName, "not_after"), acctest.CheckResourceAttrRFC3339(resourceName, "not_before"), resource.TestCheckResourceAttr(resourceName, "renewal_eligibility", "INELIGIBLE"), + resource.TestCheckResourceAttr(resourceName, "renewal_summary.#", "0"), resource.TestCheckResourceAttr(resourceName, "type", "IMPORTED"), ), }, diff --git a/website/docs/r/acm_certificate.html.markdown b/website/docs/r/acm_certificate.html.markdown index d82e6c1bc54..51678a71525 100644 --- a/website/docs/r/acm_certificate.html.markdown +++ b/website/docs/r/acm_certificate.html.markdown @@ -157,6 +157,7 @@ In addition to all arguments above, the following attributes are exported: * `not_after` - Expiration date and time of the certificate. * `not_before` - Start of the validity period of the certificate. * `renewal_eligibility` - Whether the certificate is eligible for renewal. +* `renewal_summary` - Contains information about the status of ACM's [managed renewal](https://docs.aws.amazon.com/acm/latest/userguide/acm-renewal.html) for the certificate. * `status` - Status of the certificate. * `type` - Source of the certificate. * `tags_all` - Map of tags assigned to the resource, including those inherited from the provider [`default_tags` configuration block](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#default_tags-configuration-block). @@ -169,6 +170,11 @@ Domain validation objects export the following attributes: * `resource_record_type` - The type of DNS record to create * `resource_record_value` - The value the DNS record needs to have +Renewal summary objects export the following attributes: + +* `renewal_status` - The status of ACM's managed renewal of the certificate +* `renewal_status_reason` - The reason that a renewal request was unsuccessful + [1]: https://www.terraform.io/docs/configuration/meta-arguments/lifecycle.html ## Import From adc2619b0d14789328f5d3e9ca3c8979740231bc Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Tue, 20 Sep 2022 17:19:30 -0700 Subject: [PATCH 13/26] Cleanup --- internal/service/acm/certificate_test.go | 35 ++++++++++++++++++------ 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/internal/service/acm/certificate_test.go b/internal/service/acm/certificate_test.go index b7a2b76d55e..78a05269655 100644 --- a/internal/service/acm/certificate_test.go +++ b/internal/service/acm/certificate_test.go @@ -8,6 +8,7 @@ import ( "strings" "testing" + "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/acm" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" @@ -39,7 +40,9 @@ func TestAccACMCertificate_emailValidation(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "status", acm.CertificateStatusPendingValidation), resource.TestCheckResourceAttr(resourceName, "subject_alternative_names.#", "1"), resource.TestCheckTypeSetElemAttr(resourceName, "subject_alternative_names.*", domain), - resource.TestCheckResourceAttr(resourceName, "type", "AMAZON_ISSUED"), + resource.TestCheckResourceAttr(resourceName, "type", acm.CertificateTypeAmazonIssued), + resource.TestCheckResourceAttr(resourceName, "renewal_eligibility", acm.RenewalEligibilityIneligible), + resource.TestCheckResourceAttr(resourceName, "renewal_summary.#", "0"), acctest.CheckResourceAttrGreaterThanValue(resourceName, "validation_emails.#", "0"), resource.TestMatchResourceAttr(resourceName, "validation_emails.0", regexp.MustCompile(`^[^@]+@.+$`)), resource.TestCheckResourceAttr(resourceName, "validation_method", acm.ValidationMethodEmail), @@ -81,7 +84,9 @@ func TestAccACMCertificate_dnsValidation(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "status", acm.CertificateStatusPendingValidation), resource.TestCheckResourceAttr(resourceName, "subject_alternative_names.#", "1"), resource.TestCheckTypeSetElemAttr(resourceName, "subject_alternative_names.*", domain), - resource.TestCheckResourceAttr(resourceName, "type", "AMAZON_ISSUED"), + resource.TestCheckResourceAttr(resourceName, "type", acm.CertificateTypeAmazonIssued), + resource.TestCheckResourceAttr(resourceName, "renewal_eligibility", acm.RenewalEligibilityIneligible), + resource.TestCheckResourceAttr(resourceName, "renewal_summary.#", "0"), resource.TestCheckResourceAttr(resourceName, "validation_emails.#", "0"), resource.TestCheckResourceAttr(resourceName, "validation_method", acm.ValidationMethodDns), resource.TestCheckResourceAttr(resourceName, "validation_option.#", "0"), @@ -196,12 +201,12 @@ func TestAccACMCertificate_privateCert(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "domain_validation_options.#", "0"), resource.TestCheckResourceAttr(resourceName, "not_after", ""), resource.TestCheckResourceAttr(resourceName, "not_before", ""), - resource.TestCheckResourceAttr(resourceName, "renewal_eligibility", "INELIGIBLE"), + resource.TestCheckResourceAttr(resourceName, "renewal_eligibility", acm.RenewalEligibilityIneligible), resource.TestCheckResourceAttr(resourceName, "renewal_summary.#", "0"), resource.TestCheckResourceAttr(resourceName, "status", acm.CertificateStatusFailed), // FailureReason: PCA_INVALID_STATE (PCA State: PENDING_CERTIFICATE) resource.TestCheckResourceAttr(resourceName, "subject_alternative_names.#", "1"), resource.TestCheckTypeSetElemAttr(resourceName, "subject_alternative_names.*", certificateDomainName), - resource.TestCheckResourceAttr(resourceName, "type", "PRIVATE"), + resource.TestCheckResourceAttr(resourceName, "type", acm.CertificateTypePrivate), resource.TestCheckResourceAttr(resourceName, "validation_emails.#", "0"), resource.TestCheckResourceAttr(resourceName, "validation_method", "NONE"), resource.TestCheckResourceAttr(resourceName, "validation_option.#", "0"), @@ -619,7 +624,7 @@ func TestAccACMCertificate_Imported_domainName(t *testing.T) { newCaCertificate := acctest.TLSRSAX509SelfSignedCACertificatePEM(newCaKey) newCertificate := acctest.TLSRSAX509LocallySignedCertificatePEM(newCaKey, newCaCertificate, key, commonName) withoutChainDomain := acctest.RandomDomainName() - var v acm.CertificateDetail + var v1, v2, v3 acm.CertificateDetail resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, @@ -630,7 +635,8 @@ func TestAccACMCertificate_Imported_domainName(t *testing.T) { { Config: testAccCertificateConfig_privateKey(certificate, key, caCertificate), Check: resource.ComposeTestCheckFunc( - testAccCheckCertificateExists(resourceName, &v), + testAccCheckCertificateExists(resourceName, &v1), + resource.TestCheckResourceAttr(resourceName, "status", acm.CertificateStatusIssued), resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), resource.TestCheckResourceAttr(resourceName, "domain_name", commonName), ), @@ -638,6 +644,8 @@ func TestAccACMCertificate_Imported_domainName(t *testing.T) { { Config: testAccCertificateConfig_privateKey(newCertificate, key, newCaCertificate), Check: resource.ComposeTestCheckFunc( + testAccCheckCertificateExists(resourceName, &v2), + testAccCheckCertficateNotRecreated(&v1, &v2), resource.TestCheckResourceAttr(resourceName, "status", acm.CertificateStatusIssued), resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), resource.TestCheckResourceAttr(resourceName, "domain_name", commonName), @@ -646,6 +654,8 @@ func TestAccACMCertificate_Imported_domainName(t *testing.T) { { Config: testAccCertificateConfig_privateKeyNoChain(withoutChainDomain), Check: resource.ComposeTestCheckFunc( + testAccCheckCertificateExists(resourceName, &v3), + testAccCheckCertficateNotRecreated(&v2, &v3), resource.TestCheckResourceAttr(resourceName, "status", acm.CertificateStatusIssued), resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), resource.TestCheckResourceAttr(resourceName, "domain_name", withoutChainDomain), @@ -685,9 +695,9 @@ func TestAccACMCertificate_Imported_validityDates(t *testing.T) { testAccCheckCertificateExists(resourceName, &v), acctest.CheckResourceAttrRFC3339(resourceName, "not_after"), acctest.CheckResourceAttrRFC3339(resourceName, "not_before"), - resource.TestCheckResourceAttr(resourceName, "renewal_eligibility", "INELIGIBLE"), + resource.TestCheckResourceAttr(resourceName, "renewal_eligibility", acm.RenewalEligibilityIneligible), resource.TestCheckResourceAttr(resourceName, "renewal_summary.#", "0"), - resource.TestCheckResourceAttr(resourceName, "type", "IMPORTED"), + resource.TestCheckResourceAttr(resourceName, "type", acm.CertificateTypeImported), ), }, { @@ -836,6 +846,15 @@ func testAccCheckCertificateDestroy(s *terraform.State) error { return nil } +func testAccCheckCertficateNotRecreated(v1, v2 *acm.CertificateDetail) resource.TestCheckFunc { + return func(s *terraform.State) error { + if aws.StringValue(v1.CertificateArn) != aws.StringValue(v2.CertificateArn) { + return fmt.Errorf("ACM Certificate recreated") + } + return nil + } +} + func testAccCertificateConfig_basic(domainName, validationMethod string) string { return fmt.Sprintf(` resource "aws_acm_certificate" "test" { From ca308525a794852aa41ce3a37b1f51449e061034 Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Wed, 21 Sep 2022 12:23:38 -0700 Subject: [PATCH 14/26] Updates `TestAccACMCertificate_privateCert` to successfully issue certificate and export the certificate to make in eligible for renewal --- internal/service/acm/certificate_test.go | 63 +++++++++++++++++++++--- 1 file changed, 55 insertions(+), 8 deletions(-) diff --git a/internal/service/acm/certificate_test.go b/internal/service/acm/certificate_test.go index 78a05269655..70f805ec506 100644 --- a/internal/service/acm/certificate_test.go +++ b/internal/service/acm/certificate_test.go @@ -183,7 +183,7 @@ func TestAccACMCertificate_privateCert(t *testing.T) { resourceName := "aws_acm_certificate.test" commonName := acctest.RandomDomain() certificateDomainName := commonName.RandomSubdomain().String() - var v acm.CertificateDetail + var v1, v2 acm.CertificateDetail resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, @@ -193,17 +193,17 @@ func TestAccACMCertificate_privateCert(t *testing.T) { Steps: []resource.TestStep{ { Config: testAccCertificateConfig_privateCert(commonName.String(), certificateDomainName), - Check: resource.ComposeTestCheckFunc( - testAccCheckCertificateExists(resourceName, &v), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckCertificateExists(resourceName, &v1), acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "acm", regexp.MustCompile("certificate/.+$")), resource.TestCheckResourceAttrPair(resourceName, "certificate_authority_arn", certificateAuthorityResourceName, "arn"), resource.TestCheckResourceAttr(resourceName, "domain_name", certificateDomainName), resource.TestCheckResourceAttr(resourceName, "domain_validation_options.#", "0"), - resource.TestCheckResourceAttr(resourceName, "not_after", ""), - resource.TestCheckResourceAttr(resourceName, "not_before", ""), + acctest.CheckResourceAttrRFC3339(resourceName, "not_after"), + acctest.CheckResourceAttrRFC3339(resourceName, "not_before"), resource.TestCheckResourceAttr(resourceName, "renewal_eligibility", acm.RenewalEligibilityIneligible), resource.TestCheckResourceAttr(resourceName, "renewal_summary.#", "0"), - resource.TestCheckResourceAttr(resourceName, "status", acm.CertificateStatusFailed), // FailureReason: PCA_INVALID_STATE (PCA State: PENDING_CERTIFICATE) + resource.TestCheckResourceAttr(resourceName, "status", acm.CertificateStatusIssued), resource.TestCheckResourceAttr(resourceName, "subject_alternative_names.#", "1"), resource.TestCheckTypeSetElemAttr(resourceName, "subject_alternative_names.*", certificateDomainName), resource.TestCheckResourceAttr(resourceName, "type", acm.CertificateTypePrivate), @@ -217,6 +217,27 @@ func TestAccACMCertificate_privateCert(t *testing.T) { ImportState: true, ImportStateVerify: true, }, + { + PreConfig: func() { + conn := acctest.Provider.Meta().(*conns.AWSClient).ACMConn + + _, err := conn.ExportCertificate(&acm.ExportCertificateInput{ + CertificateArn: v1.CertificateArn, + Passphrase: []byte("passphrase"), + }) + if err != nil { + t.Fatalf("exporting ACM Certificate (%s): %s", aws.StringValue(v1.CertificateArn), err) + } + }, + Config: testAccCertificateConfig_privateCert(commonName.String(), certificateDomainName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckCertificateExists(resourceName, &v2), + resource.TestCheckResourceAttr(resourceName, "renewal_eligibility", acm.RenewalEligibilityEligible), + resource.TestCheckResourceAttr(resourceName, "renewal_summary.#", "0"), + resource.TestCheckResourceAttr(resourceName, "status", acm.CertificateStatusIssued), + resource.TestCheckResourceAttr(resourceName, "type", acm.CertificateTypePrivate), + ), + }, }, }) } @@ -880,6 +901,15 @@ resource "aws_acm_certificate" "test" { func testAccCertificateConfig_privateCert(commonName, certificateDomainName string) string { return fmt.Sprintf(` +resource "aws_acm_certificate" "test" { + domain_name = %[2]q + certificate_authority_arn = aws_acmpca_certificate_authority.test.arn + + depends_on = [ + aws_acmpca_certificate_authority_certificate.test, + ] +} + resource "aws_acmpca_certificate_authority" "test" { permanent_deletion_time_in_days = 7 type = "ROOT" @@ -894,10 +924,27 @@ resource "aws_acmpca_certificate_authority" "test" { } } -resource "aws_acm_certificate" "test" { - domain_name = %[2]q +resource "aws_acmpca_certificate" "test" { + certificate_authority_arn = aws_acmpca_certificate_authority.test.arn + certificate_signing_request = aws_acmpca_certificate_authority.test.certificate_signing_request + signing_algorithm = "SHA512WITHRSA" + + template_arn = "arn:${data.aws_partition.current.partition}:acm-pca:::template/RootCACertificate/V1" + + validity { + type = "YEARS" + value = 2 + } +} + +resource "aws_acmpca_certificate_authority_certificate" "test" { certificate_authority_arn = aws_acmpca_certificate_authority.test.arn + + certificate = aws_acmpca_certificate.test.certificate + certificate_chain = aws_acmpca_certificate.test.certificate_chain } + +data "aws_partition" "current" {} `, commonName, certificateDomainName) } From eb0c5c724b50a28af09a72e3aa3ad72f325c6c99 Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Wed, 21 Sep 2022 12:23:57 -0700 Subject: [PATCH 15/26] Uses `ComposeAggregateTestCheckFunc` --- internal/service/acm/certificate_test.go | 36 ++++++++++++------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/internal/service/acm/certificate_test.go b/internal/service/acm/certificate_test.go index 70f805ec506..f680c3e4e8f 100644 --- a/internal/service/acm/certificate_test.go +++ b/internal/service/acm/certificate_test.go @@ -72,7 +72,7 @@ func TestAccACMCertificate_dnsValidation(t *testing.T) { Steps: []resource.TestStep{ { Config: testAccCertificateConfig_basic(domain, acm.ValidationMethodDns), - Check: resource.ComposeTestCheckFunc( + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckCertificateExists(resourceName, &v), acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "acm", regexp.MustCompile("certificate/.+$")), resource.TestCheckResourceAttr(resourceName, "domain_name", domain), @@ -114,7 +114,7 @@ func TestAccACMCertificate_root(t *testing.T) { Steps: []resource.TestStep{ { Config: testAccCertificateConfig_basic(rootDomain, acm.ValidationMethodDns), - Check: resource.ComposeTestCheckFunc( + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckCertificateExists(resourceName, &v), acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "acm", regexp.MustCompile("certificate/.+$")), resource.TestCheckResourceAttr(resourceName, "domain_name", rootDomain), @@ -276,7 +276,7 @@ func TestAccACMCertificate_rootAndWildcardSan(t *testing.T) { Steps: []resource.TestStep{ { Config: testAccCertificateConfig_subjectAlternativeNames(rootDomain, strconv.Quote(wildcardDomain), acm.ValidationMethodDns), - Check: resource.ComposeTestCheckFunc( + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckCertificateExists(resourceName, &v), acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "acm", regexp.MustCompile("certificate/.+$")), resource.TestCheckResourceAttr(resourceName, "domain_name", rootDomain), @@ -339,7 +339,7 @@ func TestAccACMCertificate_San_single(t *testing.T) { Steps: []resource.TestStep{ { Config: testAccCertificateConfig_subjectAlternativeNames(domain, strconv.Quote(sanDomain), acm.ValidationMethodDns), - Check: resource.ComposeTestCheckFunc( + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckCertificateExists(resourceName, &v), acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "acm", regexp.MustCompile("certificate/.+$")), resource.TestCheckResourceAttr(resourceName, "domain_name", domain), @@ -385,7 +385,7 @@ func TestAccACMCertificate_San_multiple(t *testing.T) { Steps: []resource.TestStep{ { Config: testAccCertificateConfig_subjectAlternativeNames(domain, fmt.Sprintf("%q, %q", sanDomain1, sanDomain2), acm.ValidationMethodDns), - Check: resource.ComposeTestCheckFunc( + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckCertificateExists(resourceName, &v), acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "acm", regexp.MustCompile("certificate/.+$")), resource.TestCheckResourceAttr(resourceName, "domain_name", domain), @@ -435,7 +435,7 @@ func TestAccACMCertificate_San_trailingPeriod(t *testing.T) { Steps: []resource.TestStep{ { Config: testAccCertificateConfig_subjectAlternativeNames(domain, strconv.Quote(sanDomain), acm.ValidationMethodDns), - Check: resource.ComposeTestCheckFunc( + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckCertificateExists(resourceName, &v), acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "acm", regexp.MustCompile(`certificate/.+`)), resource.TestCheckResourceAttr(resourceName, "domain_name", domain), @@ -524,7 +524,7 @@ func TestAccACMCertificate_wildcard(t *testing.T) { Steps: []resource.TestStep{ { Config: testAccCertificateConfig_basic(wildcardDomain, acm.ValidationMethodDns), - Check: resource.ComposeTestCheckFunc( + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckCertificateExists(resourceName, &v), acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "acm", regexp.MustCompile("certificate/.+$")), resource.TestCheckResourceAttr(resourceName, "domain_name", wildcardDomain), @@ -563,7 +563,7 @@ func TestAccACMCertificate_wildcardAndRootSan(t *testing.T) { Steps: []resource.TestStep{ { Config: testAccCertificateConfig_subjectAlternativeNames(wildcardDomain, strconv.Quote(rootDomain), acm.ValidationMethodDns), - Check: resource.ComposeTestCheckFunc( + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckCertificateExists(resourceName, &v), acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "acm", regexp.MustCompile("certificate/.+$")), resource.TestCheckResourceAttr(resourceName, "domain_name", wildcardDomain), @@ -606,7 +606,7 @@ func TestAccACMCertificate_disableCTLogging(t *testing.T) { Steps: []resource.TestStep{ { Config: testAccCertificateConfig_disableCTLogging(rootDomain, acm.ValidationMethodDns), - Check: resource.ComposeTestCheckFunc( + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckCertificateExists(resourceName, &v), acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "acm", regexp.MustCompile("certificate/.+$")), resource.TestCheckResourceAttr(resourceName, "domain_name", rootDomain), @@ -655,7 +655,7 @@ func TestAccACMCertificate_Imported_domainName(t *testing.T) { Steps: []resource.TestStep{ { Config: testAccCertificateConfig_privateKey(certificate, key, caCertificate), - Check: resource.ComposeTestCheckFunc( + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckCertificateExists(resourceName, &v1), resource.TestCheckResourceAttr(resourceName, "status", acm.CertificateStatusIssued), resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), @@ -664,7 +664,7 @@ func TestAccACMCertificate_Imported_domainName(t *testing.T) { }, { Config: testAccCertificateConfig_privateKey(newCertificate, key, newCaCertificate), - Check: resource.ComposeTestCheckFunc( + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckCertificateExists(resourceName, &v2), testAccCheckCertficateNotRecreated(&v1, &v2), resource.TestCheckResourceAttr(resourceName, "status", acm.CertificateStatusIssued), @@ -674,7 +674,7 @@ func TestAccACMCertificate_Imported_domainName(t *testing.T) { }, { Config: testAccCertificateConfig_privateKeyNoChain(withoutChainDomain), - Check: resource.ComposeTestCheckFunc( + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckCertificateExists(resourceName, &v3), testAccCheckCertficateNotRecreated(&v2, &v3), resource.TestCheckResourceAttr(resourceName, "status", acm.CertificateStatusIssued), @@ -712,7 +712,7 @@ func TestAccACMCertificate_Imported_validityDates(t *testing.T) { Steps: []resource.TestStep{ { Config: testAccCertificateConfig_privateKey(certificate, key, caCertificate), - Check: resource.ComposeTestCheckFunc( + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckCertificateExists(resourceName, &v), acctest.CheckResourceAttrRFC3339(resourceName, "not_after"), acctest.CheckResourceAttrRFC3339(resourceName, "not_before"), @@ -745,7 +745,7 @@ func TestAccACMCertificate_Imported_ipAddress(t *testing.T) { // Reference: http Steps: []resource.TestStep{ { Config: testAccCertificateConfig_privateKeyNoChain("1.2.3.4"), - Check: resource.ComposeTestCheckFunc( + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckCertificateExists(resourceName, &v), resource.TestCheckResourceAttr(resourceName, "domain_name", ""), resource.TestCheckResourceAttr(resourceName, "status", acm.CertificateStatusIssued), @@ -780,7 +780,7 @@ func TestAccACMCertificate_PrivateKey_tags(t *testing.T) { Steps: []resource.TestStep{ { Config: testAccCertificateConfig_tags1(certificate1, key1, "key1", "value1"), - Check: resource.ComposeTestCheckFunc( + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckCertificateExists(resourceName, &v), resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), @@ -794,7 +794,7 @@ func TestAccACMCertificate_PrivateKey_tags(t *testing.T) { }, { Config: testAccCertificateConfig_tags2(certificate1, key1, "key1", "value1updated", "key2", "value2"), - Check: resource.ComposeTestCheckFunc( + Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1updated"), resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), @@ -802,14 +802,14 @@ func TestAccACMCertificate_PrivateKey_tags(t *testing.T) { }, { Config: testAccCertificateConfig_tags1(certificate1, key1, "key2", "value2"), - Check: resource.ComposeTestCheckFunc( + Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), ), }, { Config: testAccCertificateConfig_tags1(certificate2, key2, "key1", "value1"), - Check: resource.ComposeTestCheckFunc( + Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), ), From 7876ec84243c431bdc9f4cc8a94edeac991ba0a8 Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Fri, 23 Sep 2022 14:46:26 -0700 Subject: [PATCH 16/26] Adds check for status after certificate renewal --- internal/service/acm/certificate_test.go | 31 +++++++++++++++++++++--- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/internal/service/acm/certificate_test.go b/internal/service/acm/certificate_test.go index f680c3e4e8f..dbf31bae0ee 100644 --- a/internal/service/acm/certificate_test.go +++ b/internal/service/acm/certificate_test.go @@ -178,7 +178,7 @@ func TestAccACMCertificate_validationOptions(t *testing.T) { }) } -func TestAccACMCertificate_privateCert(t *testing.T) { +func TestAccACMCertificate_privateCertificate(t *testing.T) { certificateAuthorityResourceName := "aws_acmpca_certificate_authority.test" resourceName := "aws_acm_certificate.test" commonName := acctest.RandomDomain() @@ -192,7 +192,7 @@ func TestAccACMCertificate_privateCert(t *testing.T) { CheckDestroy: testAccCheckCertificateDestroy, Steps: []resource.TestStep{ { - Config: testAccCertificateConfig_privateCert(commonName.String(), certificateDomainName), + Config: testAccCertificateConfig_privateCertificate(commonName.String(), certificateDomainName), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckCertificateExists(resourceName, &v1), acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "acm", regexp.MustCompile("certificate/.+$")), @@ -229,7 +229,7 @@ func TestAccACMCertificate_privateCert(t *testing.T) { t.Fatalf("exporting ACM Certificate (%s): %s", aws.StringValue(v1.CertificateArn), err) } }, - Config: testAccCertificateConfig_privateCert(commonName.String(), certificateDomainName), + Config: testAccCertificateConfig_privateCertificate(commonName.String(), certificateDomainName), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckCertificateExists(resourceName, &v2), resource.TestCheckResourceAttr(resourceName, "renewal_eligibility", acm.RenewalEligibilityEligible), @@ -238,6 +238,29 @@ func TestAccACMCertificate_privateCert(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "type", acm.CertificateTypePrivate), ), }, + { + PreConfig: func() { + conn := acctest.Provider.Meta().(*conns.AWSClient).ACMConn + + ctx := context.Background() + _, err := conn.RenewCertificateWithContext(ctx, &acm.RenewCertificateInput{ + CertificateArn: v1.CertificateArn, + }) + if err != nil { + t.Fatalf("renewing ACM Certificate (%s): %s", aws.StringValue(v1.CertificateArn), err) + } + }, + Config: testAccCertificateConfig_privateCertificate(commonName.String(), certificateDomainName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckCertificateExists(resourceName, &v2), + resource.TestCheckResourceAttr(resourceName, "renewal_eligibility", acm.RenewalEligibilityEligible), + resource.TestCheckResourceAttr(resourceName, "renewal_summary.#", "1"), + resource.TestCheckResourceAttr(resourceName, "renewal_summary.0.renewal_status", acm.RenewalStatusPendingAutoRenewal), + resource.TestCheckResourceAttr(resourceName, "renewal_summary.0.renewal_status_reason", ""), + resource.TestCheckResourceAttr(resourceName, "status", acm.CertificateStatusIssued), + resource.TestCheckResourceAttr(resourceName, "type", acm.CertificateTypePrivate), + ), + }, }, }) } @@ -899,7 +922,7 @@ resource "aws_acm_certificate" "test" { `, rootDomainName, domainName) } -func testAccCertificateConfig_privateCert(commonName, certificateDomainName string) string { +func testAccCertificateConfig_privateCertificate(commonName, certificateDomainName string) string { return fmt.Sprintf(` resource "aws_acm_certificate" "test" { domain_name = %[2]q From fc990ad437ad706b04c12b6c0f68f5b5b5788cc3 Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Fri, 23 Sep 2022 17:19:28 -0700 Subject: [PATCH 17/26] Adds tests for private certificates with and without permissions to renew a certificate --- internal/service/acm/certificate.go | 40 +++++ internal/service/acm/certificate_test.go | 197 ++++++++++++++++++++++- 2 files changed, 235 insertions(+), 2 deletions(-) diff --git a/internal/service/acm/certificate.go b/internal/service/acm/certificate.go index d219c2c705c..286d708e122 100644 --- a/internal/service/acm/certificate.go +++ b/internal/service/acm/certificate.go @@ -807,3 +807,43 @@ func waitCertificateDomainValidationsAvailable(ctx context.Context, conn *acm.AC return nil, err } + +func statusCertificateRenewal(ctx context.Context, conn *acm.ACM, arn string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + certificate, err := FindCertificateByARN(ctx, conn, arn) + + if tfresource.NotFound(err) { + return nil, "", nil + } + + if err != nil { + return nil, "", err + } + + if certificate.RenewalSummary == nil { + return nil, "", nil + } + if aws.StringValue(certificate.RenewalSummary.RenewalStatus) == acm.RenewalStatusFailed { + return certificate, acm.RenewalStatusFailed, fmt.Errorf("renewing ACM Certificate (%s) failed: %s", arn, aws.StringValue(certificate.RenewalSummary.RenewalStatusReason)) + } + + return certificate, aws.StringValue(certificate.RenewalSummary.RenewalStatus), nil + } +} + +func WaitCertificateRenewed(ctx context.Context, conn *acm.ACM, arn string, timeout time.Duration) (*acm.CertificateDetail, error) { + stateConf := &resource.StateChangeConf{ + Pending: []string{acm.RenewalStatusPendingAutoRenewal}, + Target: []string{acm.RenewalStatusSuccess}, + Refresh: statusCertificateRenewal(ctx, conn, arn), + Timeout: timeout, + } + + outputRaw, err := stateConf.WaitForStateContext(ctx) + + if output, ok := outputRaw.(*acm.CertificateDetail); ok { + return output, err + } + + return nil, err +} diff --git a/internal/service/acm/certificate_test.go b/internal/service/acm/certificate_test.go index dbf31bae0ee..e1010ff2de7 100644 --- a/internal/service/acm/certificate_test.go +++ b/internal/service/acm/certificate_test.go @@ -7,6 +7,7 @@ import ( "strconv" "strings" "testing" + "time" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/acm" @@ -37,6 +38,8 @@ func TestAccACMCertificate_emailValidation(t *testing.T) { acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "acm", regexp.MustCompile("certificate/.+$")), resource.TestCheckResourceAttr(resourceName, "domain_name", domain), resource.TestCheckResourceAttr(resourceName, "domain_validation_options.#", "0"), + resource.TestCheckResourceAttr(resourceName, "not_after", ""), + resource.TestCheckResourceAttr(resourceName, "not_before", ""), resource.TestCheckResourceAttr(resourceName, "status", acm.CertificateStatusPendingValidation), resource.TestCheckResourceAttr(resourceName, "subject_alternative_names.#", "1"), resource.TestCheckTypeSetElemAttr(resourceName, "subject_alternative_names.*", domain), @@ -81,6 +84,8 @@ func TestAccACMCertificate_dnsValidation(t *testing.T) { "domain_name": domain, "resource_record_type": "CNAME", }), + resource.TestCheckResourceAttr(resourceName, "not_after", ""), + resource.TestCheckResourceAttr(resourceName, "not_before", ""), resource.TestCheckResourceAttr(resourceName, "status", acm.CertificateStatusPendingValidation), resource.TestCheckResourceAttr(resourceName, "subject_alternative_names.#", "1"), resource.TestCheckTypeSetElemAttr(resourceName, "subject_alternative_names.*", domain), @@ -183,7 +188,7 @@ func TestAccACMCertificate_privateCertificate(t *testing.T) { resourceName := "aws_acm_certificate.test" commonName := acctest.RandomDomain() certificateDomainName := commonName.RandomSubdomain().String() - var v1, v2 acm.CertificateDetail + var v1, v2, v3 acm.CertificateDetail resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, @@ -238,6 +243,11 @@ func TestAccACMCertificate_privateCertificate(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "type", acm.CertificateTypePrivate), ), }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, { PreConfig: func() { conn := acctest.Provider.Meta().(*conns.AWSClient).ACMConn @@ -251,9 +261,106 @@ func TestAccACMCertificate_privateCertificate(t *testing.T) { } }, Config: testAccCertificateConfig_privateCertificate(commonName.String(), certificateDomainName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckCertificateExists(resourceName, &v3), + resource.TestCheckResourceAttr(resourceName, "renewal_eligibility", acm.RenewalEligibilityEligible), + resource.TestCheckResourceAttr(resourceName, "renewal_summary.#", "1"), + resource.TestCheckResourceAttr(resourceName, "renewal_summary.0.renewal_status", acm.RenewalStatusPendingAutoRenewal), + resource.TestCheckResourceAttr(resourceName, "renewal_summary.0.renewal_status_reason", ""), + resource.TestCheckResourceAttr(resourceName, "status", acm.CertificateStatusIssued), + resource.TestCheckResourceAttr(resourceName, "type", acm.CertificateTypePrivate), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccACMCertificate_privateCertificate_renewable(t *testing.T) { + certificateAuthorityResourceName := "aws_acmpca_certificate_authority.test" + resourceName := "aws_acm_certificate.test" + commonName := acctest.RandomDomain() + certificateDomainName := commonName.RandomSubdomain().String() + var v1, v2, v3 acm.CertificateDetail + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, acm.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckCertificateDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCertificateConfig_privateCertificate_renewable(commonName.String(), certificateDomainName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckCertificateExists(resourceName, &v1), + acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "acm", regexp.MustCompile("certificate/.+$")), + resource.TestCheckResourceAttrPair(resourceName, "certificate_authority_arn", certificateAuthorityResourceName, "arn"), + resource.TestCheckResourceAttr(resourceName, "domain_name", certificateDomainName), + resource.TestCheckResourceAttr(resourceName, "domain_validation_options.#", "0"), + acctest.CheckResourceAttrRFC3339(resourceName, "not_after"), + acctest.CheckResourceAttrRFC3339(resourceName, "not_before"), + resource.TestCheckResourceAttr(resourceName, "renewal_eligibility", acm.RenewalEligibilityIneligible), + resource.TestCheckResourceAttr(resourceName, "renewal_summary.#", "0"), + resource.TestCheckResourceAttr(resourceName, "status", acm.CertificateStatusIssued), + resource.TestCheckResourceAttr(resourceName, "subject_alternative_names.#", "1"), + resource.TestCheckTypeSetElemAttr(resourceName, "subject_alternative_names.*", certificateDomainName), + resource.TestCheckResourceAttr(resourceName, "type", acm.CertificateTypePrivate), + resource.TestCheckResourceAttr(resourceName, "validation_emails.#", "0"), + resource.TestCheckResourceAttr(resourceName, "validation_method", "NONE"), + resource.TestCheckResourceAttr(resourceName, "validation_option.#", "0"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + PreConfig: func() { + conn := acctest.Provider.Meta().(*conns.AWSClient).ACMConn + + _, err := conn.ExportCertificate(&acm.ExportCertificateInput{ + CertificateArn: v1.CertificateArn, + Passphrase: []byte("passphrase"), + }) + if err != nil { + t.Fatalf("exporting ACM Certificate (%s): %s", aws.StringValue(v1.CertificateArn), err) + } + }, + Config: testAccCertificateConfig_privateCertificate_renewable(commonName.String(), certificateDomainName), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckCertificateExists(resourceName, &v2), resource.TestCheckResourceAttr(resourceName, "renewal_eligibility", acm.RenewalEligibilityEligible), + resource.TestCheckResourceAttr(resourceName, "renewal_summary.#", "0"), + resource.TestCheckResourceAttr(resourceName, "status", acm.CertificateStatusIssued), + resource.TestCheckResourceAttr(resourceName, "type", acm.CertificateTypePrivate), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + PreConfig: func() { + conn := acctest.Provider.Meta().(*conns.AWSClient).ACMConn + + ctx := context.Background() + _, err := conn.RenewCertificateWithContext(ctx, &acm.RenewCertificateInput{ + CertificateArn: v1.CertificateArn, + }) + if err != nil { + t.Fatalf("renewing ACM Certificate (%s): %s", aws.StringValue(v1.CertificateArn), err) + } + }, + Config: testAccCertificateConfig_privateCertificate_renewable(commonName.String(), certificateDomainName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckCertificateExists(resourceName, &v3), + resource.TestCheckResourceAttr(resourceName, "renewal_eligibility", acm.RenewalEligibilityEligible), resource.TestCheckResourceAttr(resourceName, "renewal_summary.#", "1"), resource.TestCheckResourceAttr(resourceName, "renewal_summary.0.renewal_status", acm.RenewalStatusPendingAutoRenewal), resource.TestCheckResourceAttr(resourceName, "renewal_summary.0.renewal_status_reason", ""), @@ -261,6 +368,37 @@ func TestAccACMCertificate_privateCertificate(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "type", acm.CertificateTypePrivate), ), }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + PreConfig: func() { + conn := acctest.Provider.Meta().(*conns.AWSClient).ACMConn + + ctx := context.Background() + _, err := tfacm.WaitCertificateRenewed(ctx, conn, aws.StringValue(v1.CertificateArn), 5*time.Minute) + if err != nil { + t.Fatalf("waiting for ACM Certificate (%s) renewal: %s", aws.StringValue(v1.CertificateArn), err) + } + }, + Config: testAccCertificateConfig_privateCertificate_renewable(commonName.String(), certificateDomainName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckCertificateExists(resourceName, &v3), + resource.TestCheckResourceAttr(resourceName, "renewal_eligibility", acm.RenewalEligibilityIneligible), + resource.TestCheckResourceAttr(resourceName, "renewal_summary.#", "1"), + resource.TestCheckResourceAttr(resourceName, "renewal_summary.0.renewal_status", acm.RenewalStatusSuccess), + resource.TestCheckResourceAttr(resourceName, "renewal_summary.0.renewal_status_reason", ""), + resource.TestCheckResourceAttr(resourceName, "status", acm.CertificateStatusIssued), + resource.TestCheckResourceAttr(resourceName, "type", acm.CertificateTypePrivate), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, }, }) } @@ -929,7 +1067,56 @@ resource "aws_acm_certificate" "test" { certificate_authority_arn = aws_acmpca_certificate_authority.test.arn depends_on = [ - aws_acmpca_certificate_authority_certificate.test, + aws_acmpca_certificate_authority_certificate.test, + ] +} + +resource "aws_acmpca_certificate_authority" "test" { + permanent_deletion_time_in_days = 7 + type = "ROOT" + + certificate_authority_configuration { + key_algorithm = "RSA_4096" + signing_algorithm = "SHA512WITHRSA" + + subject { + common_name = %[1]q + } + } +} + +resource "aws_acmpca_certificate" "test" { + certificate_authority_arn = aws_acmpca_certificate_authority.test.arn + certificate_signing_request = aws_acmpca_certificate_authority.test.certificate_signing_request + signing_algorithm = "SHA512WITHRSA" + + template_arn = "arn:${data.aws_partition.current.partition}:acm-pca:::template/RootCACertificate/V1" + + validity { + type = "YEARS" + value = 2 + } +} + +resource "aws_acmpca_certificate_authority_certificate" "test" { + certificate_authority_arn = aws_acmpca_certificate_authority.test.arn + + certificate = aws_acmpca_certificate.test.certificate + certificate_chain = aws_acmpca_certificate.test.certificate_chain +} + +data "aws_partition" "current" {} +`, commonName, certificateDomainName) +} + +func testAccCertificateConfig_privateCertificate_renewable(commonName, certificateDomainName string) string { + return fmt.Sprintf(` +resource "aws_acm_certificate" "test" { + domain_name = %[2]q + certificate_authority_arn = aws_acmpca_certificate_authority.test.arn + + depends_on = [ + aws_acmpca_certificate_authority_certificate.test, ] } @@ -947,6 +1134,12 @@ resource "aws_acmpca_certificate_authority" "test" { } } +resource "aws_acmpca_permission" "test" { + certificate_authority_arn = aws_acmpca_certificate_authority.test.arn + principal = "acm.amazonaws.com" + actions = ["IssueCertificate", "GetCertificate", "ListPermissions"] +} + resource "aws_acmpca_certificate" "test" { certificate_authority_arn = aws_acmpca_certificate_authority.test.arn certificate_signing_request = aws_acmpca_certificate_authority.test.certificate_signing_request From aa7be42e75640a39e6be61461fe2673b3fdc819a Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Fri, 23 Sep 2022 17:33:21 -0700 Subject: [PATCH 18/26] Adds `renewal_status_reason` for unsuccessful renewal --- internal/service/acm/certificate_test.go | 20 ++++++++++++++++++++ website/docs/r/acm_certificate.html.markdown | 2 +- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/internal/service/acm/certificate_test.go b/internal/service/acm/certificate_test.go index e1010ff2de7..a393e3dfa54 100644 --- a/internal/service/acm/certificate_test.go +++ b/internal/service/acm/certificate_test.go @@ -276,6 +276,26 @@ func TestAccACMCertificate_privateCertificate(t *testing.T) { ImportState: true, ImportStateVerify: true, }, + { + PreConfig: func() { + time.Sleep(10 * time.Second) + }, + Config: testAccCertificateConfig_privateCertificate(commonName.String(), certificateDomainName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckCertificateExists(resourceName, &v3), + resource.TestCheckResourceAttr(resourceName, "renewal_eligibility", acm.RenewalEligibilityEligible), + resource.TestCheckResourceAttr(resourceName, "renewal_summary.#", "1"), + resource.TestCheckResourceAttr(resourceName, "renewal_summary.0.renewal_status", acm.RenewalStatusPendingAutoRenewal), + resource.TestCheckResourceAttr(resourceName, "renewal_summary.0.renewal_status_reason", acm.FailureReasonPcaAccessDenied), + resource.TestCheckResourceAttr(resourceName, "status", acm.CertificateStatusIssued), + resource.TestCheckResourceAttr(resourceName, "type", acm.CertificateTypePrivate), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, }, }) } diff --git a/website/docs/r/acm_certificate.html.markdown b/website/docs/r/acm_certificate.html.markdown index 51678a71525..74f490354cd 100644 --- a/website/docs/r/acm_certificate.html.markdown +++ b/website/docs/r/acm_certificate.html.markdown @@ -173,7 +173,7 @@ Domain validation objects export the following attributes: Renewal summary objects export the following attributes: * `renewal_status` - The status of ACM's managed renewal of the certificate -* `renewal_status_reason` - The reason that a renewal request was unsuccessful +* `renewal_status_reason` - The reason that a renewal request was unsuccessful or is pending [1]: https://www.terraform.io/docs/configuration/meta-arguments/lifecycle.html From 228681915b9c9cb6d25f7098e59e97cfd31c6133 Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Mon, 26 Sep 2022 12:00:26 -0700 Subject: [PATCH 19/26] Cleanup --- internal/service/acm/certificate.go | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/internal/service/acm/certificate.go b/internal/service/acm/certificate.go index 286d708e122..e4fce61dd17 100644 --- a/internal/service/acm/certificate.go +++ b/internal/service/acm/certificate.go @@ -74,9 +74,6 @@ func ResourceCertificate() *schema.Resource { ConflictsWith: []string{"certificate_authority_arn", "domain_name", "validation_method"}, }, "domain_name": { - // AWS Provider 3.0.0 aws_route53_zone references no longer contain a - // trailing period, no longer requiring a custom StateFunc - // to prevent ACM API error Type: schema.TypeString, Optional: true, Computed: true, @@ -180,9 +177,6 @@ func ResourceCertificate() *schema.Resource { Computed: true, ForceNew: true, Elem: &schema.Schema{ - // AWS Provider 3.0.0 aws_route53_zone references no longer contain a - // trailing period, no longer requiring a custom StateFunc - // to prevent ACM API error Type: schema.TypeString, ValidateFunc: validation.All( validation.StringLenBetween(1, 253), @@ -237,9 +231,6 @@ func ResourceCertificate() *schema.Resource { // Attempt to calculate the domain validation options based on domains present in domain_name and subject_alternative_names if diff.Get("validation_method").(string) == acm.ValidationMethodDns && (diff.HasChange("domain_name") || diff.HasChange("subject_alternative_names")) { domainValidationOptionsList := []interface{}{map[string]interface{}{ - // AWS Provider 3.0 -- plan-time validation prevents "domain_name" - // argument to accept a string with trailing period; thus, trim of trailing period - // no longer required here "domain_name": diff.Get("domain_name").(string), }} @@ -252,9 +243,6 @@ func ResourceCertificate() *schema.Resource { } m := map[string]interface{}{ - // AWS Provider 3.0 -- plan-time validation prevents "subject_alternative_names" - // argument to accept strings with trailing period; thus, trim of trailing period - // no longer required here "domain_name": san, } @@ -450,8 +438,6 @@ func resourceCertificateUpdate(ctx context.Context, d *schema.ResourceData, meta conn := meta.(*conns.AWSClient).ACMConn if d.HasChanges("private_key", "certificate_body", "certificate_chain") { - // Prior to version 3.0.0 of the Terraform AWS Provider, these attributes were stored in state as hashes. - // If the changes to these attributes are only changes only match updating the state value, then skip the API call. oCBRaw, nCBRaw := d.GetChange("certificate_body") oCCRaw, nCCRaw := d.GetChange("certificate_chain") oPKRaw, nPKRaw := d.GetChange("private_key") From ceffee1ada89cd1f8d20fd918062c6fa59f54586 Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Mon, 26 Sep 2022 12:00:49 -0700 Subject: [PATCH 20/26] Clarifies documentation --- website/docs/r/acm_certificate.html.markdown | 40 +++++++++++++++---- .../docs/r/acmpca_certificate.html.markdown | 5 +++ 2 files changed, 38 insertions(+), 7 deletions(-) diff --git a/website/docs/r/acm_certificate.html.markdown b/website/docs/r/acm_certificate.html.markdown index 74f490354cd..5589980a71f 100644 --- a/website/docs/r/acm_certificate.html.markdown +++ b/website/docs/r/acm_certificate.html.markdown @@ -11,21 +11,43 @@ description: |- The ACM certificate resource allows requesting and management of certificates from the Amazon Certificate Manager. -It deals with requesting certificates and managing their attributes and life-cycle. +ACM certificates can be created in three ways: +Amazon-issued, where AWS provides the certificate authority and automatically manages renewal; +imported certificates, issued by another certificate authority; +and private certificates, issued using an ACM Private Certificate Authority. + +## Amazon-Issued Certificates + +For Amazon-issued certificates, this resource deals with requesting certificates and managing their attributes and life-cycle. This resource does not deal with validation of a certificate but can provide inputs -for other resources implementing the validation. It does not wait for a certificate to be issued. +for other resources implementing the validation. +It does not wait for a certificate to be issued. Use a [`aws_acm_certificate_validation`](acm_certificate_validation.html) resource for this. Most commonly, this resource is used together with [`aws_route53_record`](route53_record.html) and [`aws_acm_certificate_validation`](acm_certificate_validation.html) to request a DNS validated certificate, deploy the required validation records and wait for validation to complete. -Domain validation through email is also supported but should be avoided as it requires a manual step outside -of Terraform. +Domain validation through email is also supported but should be avoided as it requires a manual step outside of Terraform. It's recommended to specify `create_before_destroy = true` in a [lifecycle][1] block to replace a certificate which is currently in use (eg, by [`aws_lb_listener`](lb_listener.html)). +## Imported Certificates + +Imported certificates can be used to make certificates created with an external certificate authority available for AWS services. + +As they are not managed by AWS, imported certificates are not eligible for automatic renewal. +New certificate materials can be supplied to an existing imported certificate to update it in place. + +## Private Certificates + +Private certificates are issued by an ACM Private Cerificate Authority, which can be created using the resource type [`aws_acmpca_certificate_authority`](acmpca_certificate_authority.html). + +Private certificates created using this resource are eligible for renewal if they have been exported or associated with another AWS service. +See [managed renewal documentation](https://docs.aws.amazon.com/acm/latest/userguide/managed-renewal.html) for more information. + + ## Example Usage ### Create Certificate @@ -128,9 +150,11 @@ The following arguments are supported: * `certificate_body` - (Required) Certificate's PEM-formatted public key * `certificate_chain` - (Optional) Certificate's PEM-formatted chain * Creating a private CA issued certificate - * `domain_name` - (Required) Domain name for which the certificate should be issued * `certificate_authority_arn` - (Required) ARN of an ACM PCA - * `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. + * `domain_name` - (Required) Domain name for which the certificate should be issued. +* `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. * `tags` - (Optional) Map of tags to assign to the resource. If configured with a provider [`default_tags` configuration block](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#default_tags-configuration-block) present, tags with matching keys will overwrite those defined at the provider-level. ## options Configuration Block @@ -153,7 +177,9 @@ In addition to all arguments above, the following attributes are exported: * `id` - ARN of the certificate * `arn` - ARN of the certificate * `domain_name` - Domain name for which the certificate is issued -* `domain_validation_options` - Set of domain validation objects which can be used to complete certificate validation. Can have more than one element, e.g., if SANs are defined. Only set if `DNS`-validation was used. +* `domain_validation_options` - Set of domain validation objects which can be used to complete certificate validation. + Can have more than one element, e.g., if SANs are defined. + Only set if `DNS`-validation was used. * `not_after` - Expiration date and time of the certificate. * `not_before` - Start of the validity period of the certificate. * `renewal_eligibility` - Whether the certificate is eligible for renewal. diff --git a/website/docs/r/acmpca_certificate.html.markdown b/website/docs/r/acmpca_certificate.html.markdown index f362c255ea2..50d54adf795 100644 --- a/website/docs/r/acmpca_certificate.html.markdown +++ b/website/docs/r/acmpca_certificate.html.markdown @@ -10,6 +10,11 @@ description: |- Provides a resource to issue a certificate using AWS Certificate Manager Private Certificate Authority (ACM PCA). +Certificates created using `aws_acmpca_certificate` are not eligible for automatic renewal, +and must be replaced instead. +To issue a renewable certificate using an ACM PCA, create a [`aws_acm_certificate`](acm_certificate.html) +with the parameter `certificate_authority_arn`. + ## Example Usage ### Basic From 9e1cd1f82194587beccec315713c4c8203a97646 Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Mon, 26 Sep 2022 12:24:11 -0700 Subject: [PATCH 21/26] Reorder tests --- internal/service/acm/certificate_test.go | 65 ++++++++++++------------ 1 file changed, 33 insertions(+), 32 deletions(-) diff --git a/internal/service/acm/certificate_test.go b/internal/service/acm/certificate_test.go index a393e3dfa54..1bf874956d9 100644 --- a/internal/service/acm/certificate_test.go +++ b/internal/service/acm/certificate_test.go @@ -183,7 +183,7 @@ func TestAccACMCertificate_validationOptions(t *testing.T) { }) } -func TestAccACMCertificate_privateCertificate(t *testing.T) { +func TestAccACMCertificate_privateCertificate_renewable(t *testing.T) { certificateAuthorityResourceName := "aws_acmpca_certificate_authority.test" resourceName := "aws_acm_certificate.test" commonName := acctest.RandomDomain() @@ -197,7 +197,7 @@ func TestAccACMCertificate_privateCertificate(t *testing.T) { CheckDestroy: testAccCheckCertificateDestroy, Steps: []resource.TestStep{ { - Config: testAccCertificateConfig_privateCertificate(commonName.String(), certificateDomainName), + Config: testAccCertificateConfig_privateCertificate_renewable(commonName.String(), certificateDomainName), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckCertificateExists(resourceName, &v1), acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "acm", regexp.MustCompile("certificate/.+$")), @@ -234,7 +234,7 @@ func TestAccACMCertificate_privateCertificate(t *testing.T) { t.Fatalf("exporting ACM Certificate (%s): %s", aws.StringValue(v1.CertificateArn), err) } }, - Config: testAccCertificateConfig_privateCertificate(commonName.String(), certificateDomainName), + Config: testAccCertificateConfig_privateCertificate_renewable(commonName.String(), certificateDomainName), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckCertificateExists(resourceName, &v2), resource.TestCheckResourceAttr(resourceName, "renewal_eligibility", acm.RenewalEligibilityEligible), @@ -260,7 +260,7 @@ func TestAccACMCertificate_privateCertificate(t *testing.T) { t.Fatalf("renewing ACM Certificate (%s): %s", aws.StringValue(v1.CertificateArn), err) } }, - Config: testAccCertificateConfig_privateCertificate(commonName.String(), certificateDomainName), + Config: testAccCertificateConfig_privateCertificate_renewable(commonName.String(), certificateDomainName), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckCertificateExists(resourceName, &v3), resource.TestCheckResourceAttr(resourceName, "renewal_eligibility", acm.RenewalEligibilityEligible), @@ -278,15 +278,21 @@ func TestAccACMCertificate_privateCertificate(t *testing.T) { }, { PreConfig: func() { - time.Sleep(10 * time.Second) + conn := acctest.Provider.Meta().(*conns.AWSClient).ACMConn + + ctx := context.Background() + _, err := tfacm.WaitCertificateRenewed(ctx, conn, aws.StringValue(v1.CertificateArn), 5*time.Minute) + if err != nil { + t.Fatalf("waiting for ACM Certificate (%s) renewal: %s", aws.StringValue(v1.CertificateArn), err) + } }, - Config: testAccCertificateConfig_privateCertificate(commonName.String(), certificateDomainName), + Config: testAccCertificateConfig_privateCertificate_renewable(commonName.String(), certificateDomainName), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckCertificateExists(resourceName, &v3), - resource.TestCheckResourceAttr(resourceName, "renewal_eligibility", acm.RenewalEligibilityEligible), + resource.TestCheckResourceAttr(resourceName, "renewal_eligibility", acm.RenewalEligibilityIneligible), resource.TestCheckResourceAttr(resourceName, "renewal_summary.#", "1"), - resource.TestCheckResourceAttr(resourceName, "renewal_summary.0.renewal_status", acm.RenewalStatusPendingAutoRenewal), - resource.TestCheckResourceAttr(resourceName, "renewal_summary.0.renewal_status_reason", acm.FailureReasonPcaAccessDenied), + resource.TestCheckResourceAttr(resourceName, "renewal_summary.0.renewal_status", acm.RenewalStatusSuccess), + resource.TestCheckResourceAttr(resourceName, "renewal_summary.0.renewal_status_reason", ""), resource.TestCheckResourceAttr(resourceName, "status", acm.CertificateStatusIssued), resource.TestCheckResourceAttr(resourceName, "type", acm.CertificateTypePrivate), ), @@ -300,7 +306,7 @@ func TestAccACMCertificate_privateCertificate(t *testing.T) { }) } -func TestAccACMCertificate_privateCertificate_renewable(t *testing.T) { +func TestAccACMCertificate_privateCertificate_noRenewalPermission(t *testing.T) { certificateAuthorityResourceName := "aws_acmpca_certificate_authority.test" resourceName := "aws_acm_certificate.test" commonName := acctest.RandomDomain() @@ -314,7 +320,7 @@ func TestAccACMCertificate_privateCertificate_renewable(t *testing.T) { CheckDestroy: testAccCheckCertificateDestroy, Steps: []resource.TestStep{ { - Config: testAccCertificateConfig_privateCertificate_renewable(commonName.String(), certificateDomainName), + Config: testAccCertificateConfig_privateCertificate_noRenewalPermission(commonName.String(), certificateDomainName), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckCertificateExists(resourceName, &v1), acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "acm", regexp.MustCompile("certificate/.+$")), @@ -351,7 +357,7 @@ func TestAccACMCertificate_privateCertificate_renewable(t *testing.T) { t.Fatalf("exporting ACM Certificate (%s): %s", aws.StringValue(v1.CertificateArn), err) } }, - Config: testAccCertificateConfig_privateCertificate_renewable(commonName.String(), certificateDomainName), + Config: testAccCertificateConfig_privateCertificate_noRenewalPermission(commonName.String(), certificateDomainName), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckCertificateExists(resourceName, &v2), resource.TestCheckResourceAttr(resourceName, "renewal_eligibility", acm.RenewalEligibilityEligible), @@ -377,7 +383,7 @@ func TestAccACMCertificate_privateCertificate_renewable(t *testing.T) { t.Fatalf("renewing ACM Certificate (%s): %s", aws.StringValue(v1.CertificateArn), err) } }, - Config: testAccCertificateConfig_privateCertificate_renewable(commonName.String(), certificateDomainName), + Config: testAccCertificateConfig_privateCertificate_noRenewalPermission(commonName.String(), certificateDomainName), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckCertificateExists(resourceName, &v3), resource.TestCheckResourceAttr(resourceName, "renewal_eligibility", acm.RenewalEligibilityEligible), @@ -395,21 +401,15 @@ func TestAccACMCertificate_privateCertificate_renewable(t *testing.T) { }, { PreConfig: func() { - conn := acctest.Provider.Meta().(*conns.AWSClient).ACMConn - - ctx := context.Background() - _, err := tfacm.WaitCertificateRenewed(ctx, conn, aws.StringValue(v1.CertificateArn), 5*time.Minute) - if err != nil { - t.Fatalf("waiting for ACM Certificate (%s) renewal: %s", aws.StringValue(v1.CertificateArn), err) - } + time.Sleep(10 * time.Second) }, - Config: testAccCertificateConfig_privateCertificate_renewable(commonName.String(), certificateDomainName), + Config: testAccCertificateConfig_privateCertificate_noRenewalPermission(commonName.String(), certificateDomainName), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckCertificateExists(resourceName, &v3), - resource.TestCheckResourceAttr(resourceName, "renewal_eligibility", acm.RenewalEligibilityIneligible), + resource.TestCheckResourceAttr(resourceName, "renewal_eligibility", acm.RenewalEligibilityEligible), resource.TestCheckResourceAttr(resourceName, "renewal_summary.#", "1"), - resource.TestCheckResourceAttr(resourceName, "renewal_summary.0.renewal_status", acm.RenewalStatusSuccess), - resource.TestCheckResourceAttr(resourceName, "renewal_summary.0.renewal_status_reason", ""), + resource.TestCheckResourceAttr(resourceName, "renewal_summary.0.renewal_status", acm.RenewalStatusPendingAutoRenewal), + resource.TestCheckResourceAttr(resourceName, "renewal_summary.0.renewal_status_reason", acm.FailureReasonPcaAccessDenied), resource.TestCheckResourceAttr(resourceName, "status", acm.CertificateStatusIssued), resource.TestCheckResourceAttr(resourceName, "type", acm.CertificateTypePrivate), ), @@ -897,6 +897,7 @@ func TestAccACMCertificate_Imported_validityDates(t *testing.T) { testAccCheckCertificateExists(resourceName, &v), acctest.CheckResourceAttrRFC3339(resourceName, "not_after"), acctest.CheckResourceAttrRFC3339(resourceName, "not_before"), + resource.TestCheckResourceAttr(resourceName, "pending_renewal", "false"), resource.TestCheckResourceAttr(resourceName, "renewal_eligibility", acm.RenewalEligibilityIneligible), resource.TestCheckResourceAttr(resourceName, "renewal_summary.#", "0"), resource.TestCheckResourceAttr(resourceName, "type", acm.CertificateTypeImported), @@ -1080,7 +1081,7 @@ resource "aws_acm_certificate" "test" { `, rootDomainName, domainName) } -func testAccCertificateConfig_privateCertificate(commonName, certificateDomainName string) string { +func testAccCertificateConfig_privateCertificate_renewable(commonName, certificateDomainName string) string { return fmt.Sprintf(` resource "aws_acm_certificate" "test" { domain_name = %[2]q @@ -1105,6 +1106,12 @@ resource "aws_acmpca_certificate_authority" "test" { } } +resource "aws_acmpca_permission" "test" { + certificate_authority_arn = aws_acmpca_certificate_authority.test.arn + principal = "acm.amazonaws.com" + actions = ["IssueCertificate", "GetCertificate", "ListPermissions"] +} + resource "aws_acmpca_certificate" "test" { certificate_authority_arn = aws_acmpca_certificate_authority.test.arn certificate_signing_request = aws_acmpca_certificate_authority.test.certificate_signing_request @@ -1129,7 +1136,7 @@ data "aws_partition" "current" {} `, commonName, certificateDomainName) } -func testAccCertificateConfig_privateCertificate_renewable(commonName, certificateDomainName string) string { +func testAccCertificateConfig_privateCertificate_noRenewalPermission(commonName, certificateDomainName string) string { return fmt.Sprintf(` resource "aws_acm_certificate" "test" { domain_name = %[2]q @@ -1154,12 +1161,6 @@ resource "aws_acmpca_certificate_authority" "test" { } } -resource "aws_acmpca_permission" "test" { - certificate_authority_arn = aws_acmpca_certificate_authority.test.arn - principal = "acm.amazonaws.com" - actions = ["IssueCertificate", "GetCertificate", "ListPermissions"] -} - resource "aws_acmpca_certificate" "test" { certificate_authority_arn = aws_acmpca_certificate_authority.test.arn certificate_signing_request = aws_acmpca_certificate_authority.test.certificate_signing_request From 3641e156ba1a918dbb9020c7f31b2478455b58d4 Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Tue, 27 Sep 2022 10:41:59 -0700 Subject: [PATCH 22/26] Adds `early_renewal_duration` --- internal/service/acm/certificate.go | 77 ++++++++- internal/service/acm/certificate_test.go | 168 ++++++++++++++++++- website/docs/r/acm_certificate.html.markdown | 13 +- 3 files changed, 251 insertions(+), 7 deletions(-) diff --git a/internal/service/acm/certificate.go b/internal/service/acm/certificate.go index e4fce61dd17..2f1343ef8cd 100644 --- a/internal/service/acm/certificate.go +++ b/internal/service/acm/certificate.go @@ -36,6 +36,9 @@ const ( // This timeout is unrelated to any creation or validation of those assigned DNS records. certificateDNSValidationAssignmentTimeout = 5 * time.Minute + // CertificateRenewalTimeout is the amount of time to wait for managed renewal of a certificate + CertificateRenewalTimeout = 1 * time.Minute + certificateValidationMethodNone = "NONE" ) @@ -107,6 +110,12 @@ func ResourceCertificate() *schema.Resource { }, Set: domainValidationOptionsHash, }, + "early_renewal_duration": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: verify.ValidDuration, + ConflictsWith: []string{"certificate_body", "certificate_chain", "private_key", "validation_method"}, + }, "not_after": { Type: schema.TypeString, Computed: true, @@ -141,6 +150,10 @@ func ResourceCertificate() *schema.Resource { return old == "1" && new == "0" }, }, + "pending_renewal": { + Type: schema.TypeBool, + Computed: true, + }, "private_key": { Type: schema.TypeString, Optional: true, @@ -164,6 +177,10 @@ func ResourceCertificate() *schema.Resource { Type: schema.TypeString, Computed: true, }, + "updated_at": { + Type: schema.TypeString, + Computed: true, + }, }, }, }, @@ -227,7 +244,7 @@ func ResourceCertificate() *schema.Resource { }, CustomizeDiff: customdiff.Sequence( - func(_ context.Context, diff *schema.ResourceDiff, v interface{}) error { + func(_ context.Context, diff *schema.ResourceDiff, _ interface{}) error { // Attempt to calculate the domain validation options based on domains present in domain_name and subject_alternative_names if diff.Get("validation_method").(string) == acm.ValidationMethodDns && (diff.HasChange("domain_name") || diff.HasChange("subject_alternative_names")) { domainValidationOptionsList := []interface{}{map[string]interface{}{ @@ -270,6 +287,23 @@ func ResourceCertificate() *schema.Resource { return nil }, + func(_ context.Context, diff *schema.ResourceDiff, _ interface{}) error { + if diff.Id() == "" { + return nil + } + if v, ok := diff.Get("early_renewal_duration").(string); !(ok && v != "") { + return nil + } + + if diff.Get("pending_renewal").(bool) { + // Trigger a diff + if err := diff.SetNewComputed("pending_renewal"); err != nil { + return err + } + } + + return nil + }, verify.SetTagsDiff, ), } @@ -380,6 +414,7 @@ func resourceCertificateRead(ctx context.Context, d *schema.ResourceData, meta i d.Set("arn", certificate.CertificateArn) d.Set("certificate_authority_arn", certificate.CertificateAuthorityArn) d.Set("domain_name", certificate.DomainName) + d.Set("early_renewal_duration", d.Get("early_renewal_duration")) if err := d.Set("domain_validation_options", domainValidationOptions); err != nil { return diag.Errorf("setting domain_validation_options: %s", err) } @@ -400,6 +435,7 @@ func resourceCertificateRead(ctx context.Context, d *schema.ResourceData, meta i } else { d.Set("options", nil) } + certificateSetPendingRenewal(d, certificate) d.Set("renewal_eligibility", certificate.RenewalEligibility) if certificate.RenewalSummary != nil { if err := d.Set("renewal_summary", []interface{}{flattenRenewalSummary(certificate.RenewalSummary)}); err != nil { @@ -459,6 +495,19 @@ func resourceCertificateUpdate(ctx context.Context, d *schema.ResourceData, meta return diag.Errorf("re-importing ACM Certificate (%s): %s", d.Id(), err) } } + } else if d.Get("pending_renewal").(bool) { + log.Printf("[INFO] Renewing ACM Certificate (%s)", d.Id()) + _, err := conn.RenewCertificateWithContext(ctx, &acm.RenewCertificateInput{ + CertificateArn: aws.String(d.Get("arn").(string)), + }) + if err != nil { + return diag.Errorf("renewing ACM Certificate (%s): %s", d.Id(), err) + } + + _, err = WaitCertificateRenewed(ctx, conn, d.Get("arn").(string), CertificateRenewalTimeout) + if err != nil { + return diag.Errorf("waiting for ACM Certificate (%s) renewal: %s", d.Id(), err) + } } if d.HasChange("tags_all") { @@ -520,6 +569,28 @@ func domainValidationOptionsHash(v interface{}) int { return 0 } +func certificateSetPendingRenewal(d *schema.ResourceData, certificate *acm.CertificateDetail) { + d.Set("pending_renewal", false) + + if aws.StringValue(certificate.RenewalEligibility) != acm.RenewalEligibilityEligible || certificate.NotAfter == nil { + return + } + + earlyDuration, earlyDurationOk := d.GetOk("early_renewal_duration") + if !earlyDurationOk { + return + } + + duration, err := time.ParseDuration(earlyDuration.(string)) + if err != nil { + return + } + + earlyExpiration := aws.TimeValue(certificate.NotAfter).Add(-duration) + + d.Set("pending_renewal", time.Now().After(earlyExpiration)) +} + func expandCertificateOptions(tfMap map[string]interface{}) *acm.CertificateOptions { if tfMap == nil { return nil @@ -664,6 +735,10 @@ func flattenRenewalSummary(apiObject *acm.RenewalSummary) map[string]interface{} tfMap["renewal_status_reason"] = aws.StringValue(v) } + if v := apiObject.UpdatedAt; v != nil { + tfMap["updated_at"] = aws.TimeValue(v).Format(time.RFC3339) + } + return tfMap } diff --git a/internal/service/acm/certificate_test.go b/internal/service/acm/certificate_test.go index 1bf874956d9..855196920f3 100644 --- a/internal/service/acm/certificate_test.go +++ b/internal/service/acm/certificate_test.go @@ -38,8 +38,10 @@ func TestAccACMCertificate_emailValidation(t *testing.T) { acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "acm", regexp.MustCompile("certificate/.+$")), resource.TestCheckResourceAttr(resourceName, "domain_name", domain), resource.TestCheckResourceAttr(resourceName, "domain_validation_options.#", "0"), + resource.TestCheckResourceAttr(resourceName, "early_renewal_duration", ""), resource.TestCheckResourceAttr(resourceName, "not_after", ""), resource.TestCheckResourceAttr(resourceName, "not_before", ""), + resource.TestCheckResourceAttr(resourceName, "pending_renewal", "false"), resource.TestCheckResourceAttr(resourceName, "status", acm.CertificateStatusPendingValidation), resource.TestCheckResourceAttr(resourceName, "subject_alternative_names.#", "1"), resource.TestCheckTypeSetElemAttr(resourceName, "subject_alternative_names.*", domain), @@ -84,8 +86,10 @@ func TestAccACMCertificate_dnsValidation(t *testing.T) { "domain_name": domain, "resource_record_type": "CNAME", }), + resource.TestCheckResourceAttr(resourceName, "early_renewal_duration", ""), resource.TestCheckResourceAttr(resourceName, "not_after", ""), resource.TestCheckResourceAttr(resourceName, "not_before", ""), + resource.TestCheckResourceAttr(resourceName, "pending_renewal", "false"), resource.TestCheckResourceAttr(resourceName, "status", acm.CertificateStatusPendingValidation), resource.TestCheckResourceAttr(resourceName, "subject_alternative_names.#", "1"), resource.TestCheckTypeSetElemAttr(resourceName, "subject_alternative_names.*", domain), @@ -204,8 +208,10 @@ func TestAccACMCertificate_privateCertificate_renewable(t *testing.T) { resource.TestCheckResourceAttrPair(resourceName, "certificate_authority_arn", certificateAuthorityResourceName, "arn"), resource.TestCheckResourceAttr(resourceName, "domain_name", certificateDomainName), resource.TestCheckResourceAttr(resourceName, "domain_validation_options.#", "0"), + resource.TestCheckResourceAttr(resourceName, "early_renewal_duration", ""), acctest.CheckResourceAttrRFC3339(resourceName, "not_after"), acctest.CheckResourceAttrRFC3339(resourceName, "not_before"), + resource.TestCheckResourceAttr(resourceName, "pending_renewal", "false"), resource.TestCheckResourceAttr(resourceName, "renewal_eligibility", acm.RenewalEligibilityIneligible), resource.TestCheckResourceAttr(resourceName, "renewal_summary.#", "0"), resource.TestCheckResourceAttr(resourceName, "status", acm.CertificateStatusIssued), @@ -237,6 +243,7 @@ func TestAccACMCertificate_privateCertificate_renewable(t *testing.T) { Config: testAccCertificateConfig_privateCertificate_renewable(commonName.String(), certificateDomainName), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckCertificateExists(resourceName, &v2), + resource.TestCheckResourceAttr(resourceName, "pending_renewal", "false"), resource.TestCheckResourceAttr(resourceName, "renewal_eligibility", acm.RenewalEligibilityEligible), resource.TestCheckResourceAttr(resourceName, "renewal_summary.#", "0"), resource.TestCheckResourceAttr(resourceName, "status", acm.CertificateStatusIssued), @@ -263,10 +270,12 @@ func TestAccACMCertificate_privateCertificate_renewable(t *testing.T) { Config: testAccCertificateConfig_privateCertificate_renewable(commonName.String(), certificateDomainName), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckCertificateExists(resourceName, &v3), + resource.TestCheckResourceAttr(resourceName, "pending_renewal", "false"), resource.TestCheckResourceAttr(resourceName, "renewal_eligibility", acm.RenewalEligibilityEligible), resource.TestCheckResourceAttr(resourceName, "renewal_summary.#", "1"), resource.TestCheckResourceAttr(resourceName, "renewal_summary.0.renewal_status", acm.RenewalStatusPendingAutoRenewal), resource.TestCheckResourceAttr(resourceName, "renewal_summary.0.renewal_status_reason", ""), + acctest.CheckResourceAttrRFC3339(resourceName, "renewal_summary.0.updated_at"), resource.TestCheckResourceAttr(resourceName, "status", acm.CertificateStatusIssued), resource.TestCheckResourceAttr(resourceName, "type", acm.CertificateTypePrivate), ), @@ -281,7 +290,7 @@ func TestAccACMCertificate_privateCertificate_renewable(t *testing.T) { conn := acctest.Provider.Meta().(*conns.AWSClient).ACMConn ctx := context.Background() - _, err := tfacm.WaitCertificateRenewed(ctx, conn, aws.StringValue(v1.CertificateArn), 5*time.Minute) + _, err := tfacm.WaitCertificateRenewed(ctx, conn, aws.StringValue(v1.CertificateArn), tfacm.CertificateRenewalTimeout) if err != nil { t.Fatalf("waiting for ACM Certificate (%s) renewal: %s", aws.StringValue(v1.CertificateArn), err) } @@ -289,10 +298,12 @@ func TestAccACMCertificate_privateCertificate_renewable(t *testing.T) { Config: testAccCertificateConfig_privateCertificate_renewable(commonName.String(), certificateDomainName), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckCertificateExists(resourceName, &v3), + resource.TestCheckResourceAttr(resourceName, "pending_renewal", "false"), resource.TestCheckResourceAttr(resourceName, "renewal_eligibility", acm.RenewalEligibilityIneligible), resource.TestCheckResourceAttr(resourceName, "renewal_summary.#", "1"), resource.TestCheckResourceAttr(resourceName, "renewal_summary.0.renewal_status", acm.RenewalStatusSuccess), resource.TestCheckResourceAttr(resourceName, "renewal_summary.0.renewal_status_reason", ""), + acctest.CheckResourceAttrRFC3339(resourceName, "renewal_summary.0.updated_at"), resource.TestCheckResourceAttr(resourceName, "status", acm.CertificateStatusIssued), resource.TestCheckResourceAttr(resourceName, "type", acm.CertificateTypePrivate), ), @@ -327,8 +338,10 @@ func TestAccACMCertificate_privateCertificate_noRenewalPermission(t *testing.T) resource.TestCheckResourceAttrPair(resourceName, "certificate_authority_arn", certificateAuthorityResourceName, "arn"), resource.TestCheckResourceAttr(resourceName, "domain_name", certificateDomainName), resource.TestCheckResourceAttr(resourceName, "domain_validation_options.#", "0"), + resource.TestCheckResourceAttr(resourceName, "early_renewal_duration", ""), acctest.CheckResourceAttrRFC3339(resourceName, "not_after"), acctest.CheckResourceAttrRFC3339(resourceName, "not_before"), + resource.TestCheckResourceAttr(resourceName, "pending_renewal", "false"), resource.TestCheckResourceAttr(resourceName, "renewal_eligibility", acm.RenewalEligibilityIneligible), resource.TestCheckResourceAttr(resourceName, "renewal_summary.#", "0"), resource.TestCheckResourceAttr(resourceName, "status", acm.CertificateStatusIssued), @@ -362,6 +375,7 @@ func TestAccACMCertificate_privateCertificate_noRenewalPermission(t *testing.T) testAccCheckCertificateExists(resourceName, &v2), resource.TestCheckResourceAttr(resourceName, "renewal_eligibility", acm.RenewalEligibilityEligible), resource.TestCheckResourceAttr(resourceName, "renewal_summary.#", "0"), + resource.TestCheckResourceAttr(resourceName, "pending_renewal", "false"), resource.TestCheckResourceAttr(resourceName, "status", acm.CertificateStatusIssued), resource.TestCheckResourceAttr(resourceName, "type", acm.CertificateTypePrivate), ), @@ -386,10 +400,12 @@ func TestAccACMCertificate_privateCertificate_noRenewalPermission(t *testing.T) Config: testAccCertificateConfig_privateCertificate_noRenewalPermission(commonName.String(), certificateDomainName), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckCertificateExists(resourceName, &v3), + resource.TestCheckResourceAttr(resourceName, "pending_renewal", "false"), resource.TestCheckResourceAttr(resourceName, "renewal_eligibility", acm.RenewalEligibilityEligible), resource.TestCheckResourceAttr(resourceName, "renewal_summary.#", "1"), resource.TestCheckResourceAttr(resourceName, "renewal_summary.0.renewal_status", acm.RenewalStatusPendingAutoRenewal), resource.TestCheckResourceAttr(resourceName, "renewal_summary.0.renewal_status_reason", ""), + acctest.CheckResourceAttrRFC3339(resourceName, "renewal_summary.0.updated_at"), resource.TestCheckResourceAttr(resourceName, "status", acm.CertificateStatusIssued), resource.TestCheckResourceAttr(resourceName, "type", acm.CertificateTypePrivate), ), @@ -401,15 +417,92 @@ func TestAccACMCertificate_privateCertificate_noRenewalPermission(t *testing.T) }, { PreConfig: func() { - time.Sleep(10 * time.Second) + time.Sleep(tfacm.CertificateRenewalTimeout) }, Config: testAccCertificateConfig_privateCertificate_noRenewalPermission(commonName.String(), certificateDomainName), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckCertificateExists(resourceName, &v3), + resource.TestCheckResourceAttr(resourceName, "pending_renewal", "false"), resource.TestCheckResourceAttr(resourceName, "renewal_eligibility", acm.RenewalEligibilityEligible), resource.TestCheckResourceAttr(resourceName, "renewal_summary.#", "1"), resource.TestCheckResourceAttr(resourceName, "renewal_summary.0.renewal_status", acm.RenewalStatusPendingAutoRenewal), resource.TestCheckResourceAttr(resourceName, "renewal_summary.0.renewal_status_reason", acm.FailureReasonPcaAccessDenied), + acctest.CheckResourceAttrRFC3339(resourceName, "renewal_summary.0.updated_at"), + resource.TestCheckResourceAttr(resourceName, "status", acm.CertificateStatusIssued), + resource.TestCheckResourceAttr(resourceName, "type", acm.CertificateTypePrivate), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccACMCertificate_privateCertificate_pendingRenewal(t *testing.T) { + resourceName := "aws_acm_certificate.test" + commonName := acctest.RandomDomain() + certificateDomainName := commonName.RandomSubdomain().String() + duration := (395 * 24 * time.Hour).String() + var v1, v2 acm.CertificateDetail + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, acm.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckCertificateDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCertificateConfig_privateCertificate_pendingRenewal(commonName.String(), certificateDomainName, duration), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckCertificateExists(resourceName, &v1), + acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "acm", regexp.MustCompile("certificate/.+$")), + resource.TestCheckResourceAttr(resourceName, "early_renewal_duration", duration), + resource.TestCheckResourceAttr(resourceName, "pending_renewal", "false"), + resource.TestCheckResourceAttr(resourceName, "renewal_eligibility", acm.RenewalEligibilityIneligible), + resource.TestCheckResourceAttr(resourceName, "renewal_summary.#", "0"), + resource.TestCheckResourceAttr(resourceName, "status", acm.CertificateStatusIssued), + resource.TestCheckTypeSetElemAttr(resourceName, "subject_alternative_names.*", certificateDomainName), + resource.TestCheckResourceAttr(resourceName, "type", acm.CertificateTypePrivate), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "early_renewal_duration", + "pending_renewal", + }, + }, + { + PreConfig: func() { + conn := acctest.Provider.Meta().(*conns.AWSClient).ACMConn + + _, err := conn.ExportCertificate(&acm.ExportCertificateInput{ + CertificateArn: v1.CertificateArn, + Passphrase: []byte("passphrase"), + }) + if err != nil { + t.Fatalf("exporting ACM Certificate (%s): %s", aws.StringValue(v1.CertificateArn), err) + } + }, + // Ideally, we'd have a `RefreshOnly` test step here to validate that `pending_renewal` is true and `renewal_eligibility` is `ELIGIBLE` after exporting + // before actually performing the renewal. + // https://github.com/hashicorp/terraform-plugin-sdk/issues/1069 + Config: testAccCertificateConfig_privateCertificate_pendingRenewal(commonName.String(), certificateDomainName, duration), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckCertificateExists(resourceName, &v2), + testAccCheckCertificateRenewed(&v1, &v2), + resource.TestCheckResourceAttr(resourceName, "early_renewal_duration", duration), + resource.TestCheckResourceAttr(resourceName, "pending_renewal", "false"), + resource.TestCheckResourceAttr(resourceName, "renewal_eligibility", acm.RenewalEligibilityIneligible), + resource.TestCheckResourceAttr(resourceName, "renewal_summary.#", "1"), + resource.TestCheckResourceAttr(resourceName, "renewal_summary.0.renewal_status", acm.RenewalStatusSuccess), + resource.TestCheckResourceAttr(resourceName, "renewal_summary.0.renewal_status_reason", ""), + acctest.CheckResourceAttrRFC3339(resourceName, "renewal_summary.0.updated_at"), resource.TestCheckResourceAttr(resourceName, "status", acm.CertificateStatusIssued), resource.TestCheckResourceAttr(resourceName, "type", acm.CertificateTypePrivate), ), @@ -418,11 +511,25 @@ func TestAccACMCertificate_privateCertificate_noRenewalPermission(t *testing.T) ResourceName: resourceName, ImportState: true, ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "early_renewal_duration", + "pending_renewal", + }, }, }, }) } +func testAccCheckCertificateRenewed(i, j *acm.CertificateDetail) resource.TestCheckFunc { + return func(s *terraform.State) error { + if !aws.TimeValue(j.NotAfter).After(aws.TimeValue(i.NotAfter)) { + return fmt.Errorf("ACM Certificate not renewed: i.NotAfter=%q, j.NotAfter=%q", aws.TimeValue(i.NotAfter), aws.TimeValue(j.NotAfter)) + } + + return nil + } +} + // TestAccACMCertificate_Root_trailingPeriod updated in 3.0 to account for domain_name plan-time validation // Reference: https://github.com/hashicorp/terraform-provider-aws/issues/13510 func TestAccACMCertificate_Root_trailingPeriod(t *testing.T) { @@ -1185,6 +1292,63 @@ data "aws_partition" "current" {} `, commonName, certificateDomainName) } +func testAccCertificateConfig_privateCertificate_pendingRenewal(commonName, certificateDomainName, duration string) string { + return fmt.Sprintf(` +resource "aws_acm_certificate" "test" { + domain_name = %[2]q + certificate_authority_arn = aws_acmpca_certificate_authority.test.arn + + early_renewal_duration = %[3]q + + depends_on = [ + aws_acmpca_certificate_authority_certificate.test, + ] +} + +resource "aws_acmpca_certificate_authority" "test" { + permanent_deletion_time_in_days = 7 + type = "ROOT" + + certificate_authority_configuration { + key_algorithm = "RSA_4096" + signing_algorithm = "SHA512WITHRSA" + + subject { + common_name = %[1]q + } + } +} + +resource "aws_acmpca_permission" "test" { + certificate_authority_arn = aws_acmpca_certificate_authority.test.arn + principal = "acm.amazonaws.com" + actions = ["IssueCertificate", "GetCertificate", "ListPermissions"] +} + +resource "aws_acmpca_certificate" "test" { + certificate_authority_arn = aws_acmpca_certificate_authority.test.arn + certificate_signing_request = aws_acmpca_certificate_authority.test.certificate_signing_request + signing_algorithm = "SHA512WITHRSA" + + template_arn = "arn:${data.aws_partition.current.partition}:acm-pca:::template/RootCACertificate/V1" + + validity { + type = "YEARS" + value = 2 + } +} + +resource "aws_acmpca_certificate_authority_certificate" "test" { + certificate_authority_arn = aws_acmpca_certificate_authority.test.arn + + certificate = aws_acmpca_certificate.test.certificate + certificate_chain = aws_acmpca_certificate.test.certificate_chain +} + +data "aws_partition" "current" {} +`, commonName, certificateDomainName, duration) +} + func testAccCertificateConfig_subjectAlternativeNames(domainName, subjectAlternativeNames, validationMethod string) string { return fmt.Sprintf(` resource "aws_acm_certificate" "test" { diff --git a/website/docs/r/acm_certificate.html.markdown b/website/docs/r/acm_certificate.html.markdown index 5589980a71f..476a8268b3c 100644 --- a/website/docs/r/acm_certificate.html.markdown +++ b/website/docs/r/acm_certificate.html.markdown @@ -44,9 +44,10 @@ New certificate materials can be supplied to an existing imported certificate to Private certificates are issued by an ACM Private Cerificate Authority, which can be created using the resource type [`aws_acmpca_certificate_authority`](acmpca_certificate_authority.html). -Private certificates created using this resource are eligible for renewal if they have been exported or associated with another AWS service. +Private certificates created using this resource are eligible for managed renewal if they have been exported or associated with another AWS service. See [managed renewal documentation](https://docs.aws.amazon.com/acm/latest/userguide/managed-renewal.html) for more information. - +By default, a certificate is valid for 395 days and the managed renewal process will start 60 days before expiration. +To renew the certificate earlier than 60 days before expiration, configure `early_renewal_duration`. ## Example Usage @@ -152,6 +153,9 @@ The following arguments are supported: * Creating a private CA issued certificate * `certificate_authority_arn` - (Required) ARN of an ACM PCA * `domain_name` - (Required) Domain name for which the certificate should be issued. + * `early_renewal_duration` - (Optional) Amount of time to start automatic renewal process before expiration. + Has no effect if less than 60 days. + Represented by a string such as `2160h`. * `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. @@ -182,12 +186,13 @@ In addition to all arguments above, the following attributes are exported: Only set if `DNS`-validation was used. * `not_after` - Expiration date and time of the certificate. * `not_before` - Start of the validity period of the certificate. -* `renewal_eligibility` - Whether the certificate is eligible for renewal. +* `pending_renewal` - `true` if a Private certificate eligible for managed renewal is within the `early_renewal_duration` period. +* `renewal_eligibility` - Whether the certificate is eligible for managed renewal. * `renewal_summary` - Contains information about the status of ACM's [managed renewal](https://docs.aws.amazon.com/acm/latest/userguide/acm-renewal.html) for the certificate. * `status` - Status of the certificate. * `type` - Source of the certificate. * `tags_all` - Map of tags assigned to the resource, including those inherited from the provider [`default_tags` configuration block](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#default_tags-configuration-block). -* `validation_emails` - List of addresses that received a validation E-Mail. Only set if `EMAIL`-validation was used. +* `validation_emails` - List of addresses that received a validation email. Only set if `EMAIL` validation was used. Domain validation objects export the following attributes: From f7481f12b37dd19d8fa0a1ceae2e3011e8ec47d6 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 27 Sep 2022 14:33:41 -0400 Subject: [PATCH 23/26] Add 'early_renewal_duration' and 'pending_renewal' to CHANGELOG entry. --- .changelog/26784.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changelog/26784.txt b/.changelog/26784.txt index ab7e81e63a6..7fcc6fa3aa2 100644 --- a/.changelog/26784.txt +++ b/.changelog/26784.txt @@ -1,3 +1,3 @@ ```release-note:enhancement -resource/aws_acm_certificate: Add `renewal_eligibility`, `renewal_summary` and `type` attributes +resource/aws_acm_certificate: Add `early_renewal_duration`, `pending_renewal`, `renewal_eligibility`, `renewal_summary` and `type` attributes ``` \ No newline at end of file From 94e255abbffb2ec102f2ca9b6ac69fd37e9a618e Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Tue, 27 Sep 2022 18:07:08 -0700 Subject: [PATCH 24/26] Handles renewals when modifying `early_renewal_duration` --- internal/service/acm/certificate.go | 51 ++- internal/service/acm/certificate_test.go | 422 +++++++++++++++++++++-- 2 files changed, 424 insertions(+), 49 deletions(-) diff --git a/internal/service/acm/certificate.go b/internal/service/acm/certificate.go index 2f1343ef8cd..c9dd2956421 100644 --- a/internal/service/acm/certificate.go +++ b/internal/service/acm/certificate.go @@ -287,15 +287,22 @@ func ResourceCertificate() *schema.Resource { return nil }, - func(_ context.Context, diff *schema.ResourceDiff, _ interface{}) error { + func(_ context.Context, diff *schema.ResourceDiff, _ any) error { if diff.Id() == "" { return nil } - if v, ok := diff.Get("early_renewal_duration").(string); !(ok && v != "") { - return nil - } - if diff.Get("pending_renewal").(bool) { + if diff.HasChange("early_renewal_duration") { + if duration := diff.Get("early_renewal_duration").(string); duration == "" { + if err := diff.SetNew("pending_renewal", false); err != nil { + return err + } + } else { + if err := diff.SetNew("pending_renewal", certificateSetPendingRenewal(diff)); err != nil { + return err + } + } + } else if diff.Get("pending_renewal").(bool) { // Trigger a diff if err := diff.SetNewComputed("pending_renewal"); err != nil { return err @@ -435,7 +442,6 @@ func resourceCertificateRead(ctx context.Context, d *schema.ResourceData, meta i } else { d.Set("options", nil) } - certificateSetPendingRenewal(d, certificate) d.Set("renewal_eligibility", certificate.RenewalEligibility) if certificate.RenewalSummary != nil { if err := d.Set("renewal_summary", []interface{}{flattenRenewalSummary(certificate.RenewalSummary)}); err != nil { @@ -450,6 +456,8 @@ func resourceCertificateRead(ctx context.Context, d *schema.ResourceData, meta i d.Set("validation_emails", validationEmails) d.Set("validation_method", certificateValidationMethod(certificate)) + d.Set("pending_renewal", certificateSetPendingRenewal(d)) + tags, err := ListTagsWithContext(ctx, conn, d.Id()) if err != nil { @@ -489,6 +497,7 @@ func resourceCertificateUpdate(ctx context.Context, d *schema.ResourceData, meta input.CertificateChain = []byte(chain.(string)) } + log.Printf("[INFO] Re-importing ACM Certificate (%s)", d.Id()) _, err := conn.ImportCertificateWithContext(ctx, input) if err != nil { @@ -569,26 +578,34 @@ func domainValidationOptionsHash(v interface{}) int { return 0 } -func certificateSetPendingRenewal(d *schema.ResourceData, certificate *acm.CertificateDetail) { - d.Set("pending_renewal", false) +type resourceGetter interface { + Get(key string) any +} - if aws.StringValue(certificate.RenewalEligibility) != acm.RenewalEligibilityEligible || certificate.NotAfter == nil { - return +func certificateSetPendingRenewal(d resourceGetter) bool { + if d.Get("renewal_eligibility") != acm.RenewalEligibilityEligible { + return false } - earlyDuration, earlyDurationOk := d.GetOk("early_renewal_duration") - if !earlyDurationOk { - return + notAfterRaw := d.Get("not_after") + if notAfterRaw == nil { + return false } + notAfter, _ := time.Parse(time.RFC3339, notAfterRaw.(string)) - duration, err := time.ParseDuration(earlyDuration.(string)) + earlyDuration := d.Get("early_renewal_duration").(string) + if earlyDuration == "" { + return false + } + + duration, err := time.ParseDuration(earlyDuration) if err != nil { - return + return false } - earlyExpiration := aws.TimeValue(certificate.NotAfter).Add(-duration) + earlyExpiration := notAfter.Add(-duration) - d.Set("pending_renewal", time.Now().After(earlyExpiration)) + return time.Now().After(earlyExpiration) } func expandCertificateOptions(tfMap map[string]interface{}) *acm.CertificateOptions { diff --git a/internal/service/acm/certificate_test.go b/internal/service/acm/certificate_test.go index 855196920f3..2447d4a5d88 100644 --- a/internal/service/acm/certificate_test.go +++ b/internal/service/acm/certificate_test.go @@ -192,7 +192,7 @@ func TestAccACMCertificate_privateCertificate_renewable(t *testing.T) { resourceName := "aws_acm_certificate.test" commonName := acctest.RandomDomain() certificateDomainName := commonName.RandomSubdomain().String() - var v1, v2, v3 acm.CertificateDetail + var v1, v2, v3, v4 acm.CertificateDetail resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, @@ -243,6 +243,7 @@ func TestAccACMCertificate_privateCertificate_renewable(t *testing.T) { Config: testAccCertificateConfig_privateCertificate_renewable(commonName.String(), certificateDomainName), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckCertificateExists(resourceName, &v2), + testAccCheckCertificateNotRenewed(&v1, &v2), resource.TestCheckResourceAttr(resourceName, "pending_renewal", "false"), resource.TestCheckResourceAttr(resourceName, "renewal_eligibility", acm.RenewalEligibilityEligible), resource.TestCheckResourceAttr(resourceName, "renewal_summary.#", "0"), @@ -280,11 +281,6 @@ func TestAccACMCertificate_privateCertificate_renewable(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "type", acm.CertificateTypePrivate), ), }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - }, { PreConfig: func() { conn := acctest.Provider.Meta().(*conns.AWSClient).ACMConn @@ -297,7 +293,8 @@ func TestAccACMCertificate_privateCertificate_renewable(t *testing.T) { }, Config: testAccCertificateConfig_privateCertificate_renewable(commonName.String(), certificateDomainName), Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckCertificateExists(resourceName, &v3), + testAccCheckCertificateExists(resourceName, &v4), + testAccCheckCertificateRenewed(&v3, &v4), resource.TestCheckResourceAttr(resourceName, "pending_renewal", "false"), resource.TestCheckResourceAttr(resourceName, "renewal_eligibility", acm.RenewalEligibilityIneligible), resource.TestCheckResourceAttr(resourceName, "renewal_summary.#", "1"), @@ -410,11 +407,6 @@ func TestAccACMCertificate_privateCertificate_noRenewalPermission(t *testing.T) resource.TestCheckResourceAttr(resourceName, "type", acm.CertificateTypePrivate), ), }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - }, { PreConfig: func() { time.Sleep(tfacm.CertificateRenewalTimeout) @@ -469,13 +461,10 @@ func TestAccACMCertificate_privateCertificate_pendingRenewal(t *testing.T) { ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - ImportStateVerifyIgnore: []string{ - "early_renewal_duration", - "pending_renewal", - }, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"early_renewal_duration"}, }, { PreConfig: func() { @@ -508,26 +497,375 @@ func TestAccACMCertificate_privateCertificate_pendingRenewal(t *testing.T) { ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - ImportStateVerifyIgnore: []string{ - "early_renewal_duration", - "pending_renewal", + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"early_renewal_duration"}, + }, + }, + }) +} + +func TestAccACMCertificate_privateCertificate_addEarlyRenewalPast(t *testing.T) { + resourceName := "aws_acm_certificate.test" + commonName := acctest.RandomDomain() + certificateDomainName := commonName.RandomSubdomain().String() + duration := (395 * 24 * time.Hour).String() + var v1, v2, v3 acm.CertificateDetail + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, acm.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckCertificateDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCertificateConfig_privateCertificate_renewable(commonName.String(), certificateDomainName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckCertificateExists(resourceName, &v1), + acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "acm", regexp.MustCompile("certificate/.+$")), + resource.TestCheckResourceAttr(resourceName, "early_renewal_duration", ""), + resource.TestCheckResourceAttr(resourceName, "pending_renewal", "false"), + resource.TestCheckResourceAttr(resourceName, "renewal_eligibility", acm.RenewalEligibilityIneligible), + resource.TestCheckResourceAttr(resourceName, "renewal_summary.#", "0"), + resource.TestCheckResourceAttr(resourceName, "status", acm.CertificateStatusIssued), + resource.TestCheckTypeSetElemAttr(resourceName, "subject_alternative_names.*", certificateDomainName), + resource.TestCheckResourceAttr(resourceName, "type", acm.CertificateTypePrivate), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"early_renewal_duration"}, + }, + { + PreConfig: func() { + conn := acctest.Provider.Meta().(*conns.AWSClient).ACMConn + + _, err := conn.ExportCertificate(&acm.ExportCertificateInput{ + CertificateArn: v1.CertificateArn, + Passphrase: []byte("passphrase"), + }) + if err != nil { + t.Fatalf("exporting ACM Certificate (%s): %s", aws.StringValue(v1.CertificateArn), err) + } }, + // Ideally, we'd have a `RefreshOnly` test step here to validate that `pending_renewal` is false and `renewal_eligibility` is `ELIGIBLE` after exporting. + // https://github.com/hashicorp/terraform-plugin-sdk/issues/1069 + Config: testAccCertificateConfig_privateCertificate_renewable(commonName.String(), certificateDomainName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckCertificateExists(resourceName, &v2), + testAccCheckCertificateNotRenewed(&v1, &v2), + resource.TestCheckResourceAttr(resourceName, "early_renewal_duration", ""), + resource.TestCheckResourceAttr(resourceName, "pending_renewal", "false"), + resource.TestCheckResourceAttr(resourceName, "renewal_eligibility", acm.RenewalEligibilityEligible), + resource.TestCheckResourceAttr(resourceName, "renewal_summary.#", "0"), + resource.TestCheckResourceAttr(resourceName, "status", acm.CertificateStatusIssued), + resource.TestCheckResourceAttr(resourceName, "type", acm.CertificateTypePrivate), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"early_renewal_duration"}, + }, + { + // Ideally, we'd have a `RefreshOnly` test step here to validate that `pending_renewal` is true after setting `early_renewal_duration`. + // https://github.com/hashicorp/terraform-plugin-sdk/issues/1069 + Config: testAccCertificateConfig_privateCertificate_pendingRenewal(commonName.String(), certificateDomainName, duration), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckCertificateExists(resourceName, &v3), + testAccCheckCertificateRenewed(&v2, &v3), + resource.TestCheckResourceAttr(resourceName, "early_renewal_duration", duration), + resource.TestCheckResourceAttr(resourceName, "pending_renewal", "false"), + resource.TestCheckResourceAttr(resourceName, "renewal_eligibility", acm.RenewalEligibilityIneligible), + resource.TestCheckResourceAttr(resourceName, "renewal_summary.#", "1"), + resource.TestCheckResourceAttr(resourceName, "renewal_summary.0.renewal_status", acm.RenewalStatusSuccess), + resource.TestCheckResourceAttr(resourceName, "renewal_summary.0.renewal_status_reason", ""), + acctest.CheckResourceAttrRFC3339(resourceName, "renewal_summary.0.updated_at"), + resource.TestCheckResourceAttr(resourceName, "status", acm.CertificateStatusIssued), + resource.TestCheckResourceAttr(resourceName, "type", acm.CertificateTypePrivate), + ), }, }, }) } -func testAccCheckCertificateRenewed(i, j *acm.CertificateDetail) resource.TestCheckFunc { - return func(s *terraform.State) error { - if !aws.TimeValue(j.NotAfter).After(aws.TimeValue(i.NotAfter)) { - return fmt.Errorf("ACM Certificate not renewed: i.NotAfter=%q, j.NotAfter=%q", aws.TimeValue(i.NotAfter), aws.TimeValue(j.NotAfter)) - } +func TestAccACMCertificate_privateCertificate_addEarlyRenewalPastIneligible(t *testing.T) { + resourceName := "aws_acm_certificate.test" + commonName := acctest.RandomDomain() + certificateDomainName := commonName.RandomSubdomain().String() + duration := (395 * 24 * time.Hour).String() + var v1, v2 acm.CertificateDetail - return nil - } + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, acm.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckCertificateDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCertificateConfig_privateCertificate_renewable(commonName.String(), certificateDomainName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckCertificateExists(resourceName, &v1), + acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "acm", regexp.MustCompile("certificate/.+$")), + resource.TestCheckResourceAttr(resourceName, "early_renewal_duration", ""), + resource.TestCheckResourceAttr(resourceName, "pending_renewal", "false"), + resource.TestCheckResourceAttr(resourceName, "renewal_eligibility", acm.RenewalEligibilityIneligible), + resource.TestCheckResourceAttr(resourceName, "renewal_summary.#", "0"), + resource.TestCheckResourceAttr(resourceName, "status", acm.CertificateStatusIssued), + resource.TestCheckTypeSetElemAttr(resourceName, "subject_alternative_names.*", certificateDomainName), + resource.TestCheckResourceAttr(resourceName, "type", acm.CertificateTypePrivate), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"early_renewal_duration"}, + }, + { + // Ideally, we'd have a `RefreshOnly` test step here to validate that `pending_renewal` is true after setting `early_renewal_duration`. + // https://github.com/hashicorp/terraform-plugin-sdk/issues/1069 + Config: testAccCertificateConfig_privateCertificate_pendingRenewal(commonName.String(), certificateDomainName, duration), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckCertificateExists(resourceName, &v2), + testAccCheckCertificateNotRenewed(&v1, &v2), + resource.TestCheckResourceAttr(resourceName, "early_renewal_duration", duration), + resource.TestCheckResourceAttr(resourceName, "pending_renewal", "false"), + resource.TestCheckResourceAttr(resourceName, "renewal_eligibility", acm.RenewalEligibilityIneligible), + resource.TestCheckResourceAttr(resourceName, "renewal_summary.#", "0"), + resource.TestCheckResourceAttr(resourceName, "status", acm.CertificateStatusIssued), + resource.TestCheckResourceAttr(resourceName, "type", acm.CertificateTypePrivate), + ), + }, + }, + }) +} + +func TestAccACMCertificate_privateCertificate_addEarlyRenewalFuture(t *testing.T) { + resourceName := "aws_acm_certificate.test" + commonName := acctest.RandomDomain() + certificateDomainName := commonName.RandomSubdomain().String() + duration := (90 * 24 * time.Hour).String() + var v1, v2, v3 acm.CertificateDetail + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, acm.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckCertificateDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCertificateConfig_privateCertificate_renewable(commonName.String(), certificateDomainName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckCertificateExists(resourceName, &v1), + acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "acm", regexp.MustCompile("certificate/.+$")), + resource.TestCheckResourceAttr(resourceName, "early_renewal_duration", ""), + resource.TestCheckResourceAttr(resourceName, "pending_renewal", "false"), + resource.TestCheckResourceAttr(resourceName, "renewal_eligibility", acm.RenewalEligibilityIneligible), + resource.TestCheckResourceAttr(resourceName, "renewal_summary.#", "0"), + resource.TestCheckResourceAttr(resourceName, "status", acm.CertificateStatusIssued), + resource.TestCheckTypeSetElemAttr(resourceName, "subject_alternative_names.*", certificateDomainName), + resource.TestCheckResourceAttr(resourceName, "type", acm.CertificateTypePrivate), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"early_renewal_duration"}, + }, + { + PreConfig: func() { + conn := acctest.Provider.Meta().(*conns.AWSClient).ACMConn + + _, err := conn.ExportCertificate(&acm.ExportCertificateInput{ + CertificateArn: v1.CertificateArn, + Passphrase: []byte("passphrase"), + }) + if err != nil { + t.Fatalf("exporting ACM Certificate (%s): %s", aws.StringValue(v1.CertificateArn), err) + } + }, + // Ideally, we'd have a `RefreshOnly` test step here to validate that `pending_renewal` is false and `renewal_eligibility` is `ELIGIBLE` after exporting. + // https://github.com/hashicorp/terraform-plugin-sdk/issues/1069 + Config: testAccCertificateConfig_privateCertificate_renewable(commonName.String(), certificateDomainName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckCertificateExists(resourceName, &v2), + testAccCheckCertificateNotRenewed(&v1, &v2), + resource.TestCheckResourceAttr(resourceName, "early_renewal_duration", ""), + resource.TestCheckResourceAttr(resourceName, "pending_renewal", "false"), + resource.TestCheckResourceAttr(resourceName, "renewal_eligibility", acm.RenewalEligibilityEligible), + resource.TestCheckResourceAttr(resourceName, "renewal_summary.#", "0"), + resource.TestCheckResourceAttr(resourceName, "status", acm.CertificateStatusIssued), + resource.TestCheckResourceAttr(resourceName, "type", acm.CertificateTypePrivate), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"early_renewal_duration"}, + }, + { + // Ideally, we'd have a `RefreshOnly` test step here to validate that `pending_renewal` is true after setting `early_renewal_duration`. + // https://github.com/hashicorp/terraform-plugin-sdk/issues/1069 + Config: testAccCertificateConfig_privateCertificate_pendingRenewal(commonName.String(), certificateDomainName, duration), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckCertificateExists(resourceName, &v3), + testAccCheckCertificateNotRenewed(&v2, &v3), + resource.TestCheckResourceAttr(resourceName, "early_renewal_duration", duration), + resource.TestCheckResourceAttr(resourceName, "pending_renewal", "false"), + resource.TestCheckResourceAttr(resourceName, "renewal_eligibility", acm.RenewalEligibilityEligible), + resource.TestCheckResourceAttr(resourceName, "renewal_summary.#", "0"), + resource.TestCheckResourceAttr(resourceName, "status", acm.CertificateStatusIssued), + resource.TestCheckResourceAttr(resourceName, "type", acm.CertificateTypePrivate), + ), + }, + }, + }) +} + +func TestAccACMCertificate_privateCertificate_updateEarlyRenewalFuture(t *testing.T) { + resourceName := "aws_acm_certificate.test" + commonName := acctest.RandomDomain() + certificateDomainName := commonName.RandomSubdomain().String() + duration := (395 * 24 * time.Hour).String() + durationUpdated := (90 * 24 * time.Hour).String() + var v1, v2 acm.CertificateDetail + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, acm.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckCertificateDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCertificateConfig_privateCertificate_pendingRenewal(commonName.String(), certificateDomainName, duration), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckCertificateExists(resourceName, &v1), + acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "acm", regexp.MustCompile("certificate/.+$")), + resource.TestCheckResourceAttr(resourceName, "early_renewal_duration", duration), + resource.TestCheckResourceAttr(resourceName, "pending_renewal", "false"), + resource.TestCheckResourceAttr(resourceName, "renewal_eligibility", acm.RenewalEligibilityIneligible), + resource.TestCheckResourceAttr(resourceName, "renewal_summary.#", "0"), + resource.TestCheckResourceAttr(resourceName, "status", acm.CertificateStatusIssued), + resource.TestCheckTypeSetElemAttr(resourceName, "subject_alternative_names.*", certificateDomainName), + resource.TestCheckResourceAttr(resourceName, "type", acm.CertificateTypePrivate), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"early_renewal_duration"}, + }, + { + PreConfig: func() { + conn := acctest.Provider.Meta().(*conns.AWSClient).ACMConn + + _, err := conn.ExportCertificate(&acm.ExportCertificateInput{ + CertificateArn: v1.CertificateArn, + Passphrase: []byte("passphrase"), + }) + if err != nil { + t.Fatalf("exporting ACM Certificate (%s): %s", aws.StringValue(v1.CertificateArn), err) + } + }, + // Ideally, we'd have a `RefreshOnly` test step here to validate that `pending_renewal` is false and `renewal_eligibility` is `ELIGIBLE` after exporting. + // https://github.com/hashicorp/terraform-plugin-sdk/issues/1069 + Config: testAccCertificateConfig_privateCertificate_pendingRenewal(commonName.String(), certificateDomainName, durationUpdated), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckCertificateExists(resourceName, &v2), + testAccCheckCertificateNotRenewed(&v1, &v2), + resource.TestCheckResourceAttr(resourceName, "early_renewal_duration", durationUpdated), + resource.TestCheckResourceAttr(resourceName, "pending_renewal", "false"), + resource.TestCheckResourceAttr(resourceName, "renewal_eligibility", acm.RenewalEligibilityEligible), + resource.TestCheckResourceAttr(resourceName, "renewal_summary.#", "0"), + resource.TestCheckResourceAttr(resourceName, "status", acm.CertificateStatusIssued), + resource.TestCheckResourceAttr(resourceName, "type", acm.CertificateTypePrivate), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"early_renewal_duration"}, + }, + }, + }) +} + +func TestAccACMCertificate_privateCertificate_removeEarlyRenewal(t *testing.T) { + resourceName := "aws_acm_certificate.test" + commonName := acctest.RandomDomain() + certificateDomainName := commonName.RandomSubdomain().String() + duration := (395 * 24 * time.Hour).String() + var v1, v2 acm.CertificateDetail + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, acm.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckCertificateDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCertificateConfig_privateCertificate_pendingRenewal(commonName.String(), certificateDomainName, duration), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckCertificateExists(resourceName, &v1), + acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "acm", regexp.MustCompile("certificate/.+$")), + resource.TestCheckResourceAttr(resourceName, "early_renewal_duration", duration), + resource.TestCheckResourceAttr(resourceName, "pending_renewal", "false"), + resource.TestCheckResourceAttr(resourceName, "renewal_eligibility", acm.RenewalEligibilityIneligible), + resource.TestCheckResourceAttr(resourceName, "renewal_summary.#", "0"), + resource.TestCheckResourceAttr(resourceName, "status", acm.CertificateStatusIssued), + resource.TestCheckTypeSetElemAttr(resourceName, "subject_alternative_names.*", certificateDomainName), + resource.TestCheckResourceAttr(resourceName, "type", acm.CertificateTypePrivate), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"early_renewal_duration"}, + }, + { + PreConfig: func() { + conn := acctest.Provider.Meta().(*conns.AWSClient).ACMConn + + _, err := conn.ExportCertificate(&acm.ExportCertificateInput{ + CertificateArn: v1.CertificateArn, + Passphrase: []byte("passphrase"), + }) + if err != nil { + t.Fatalf("exporting ACM Certificate (%s): %s", aws.StringValue(v1.CertificateArn), err) + } + }, + // Ideally, we'd have a `RefreshOnly` test step here to validate that `pending_renewal` is false and `renewal_eligibility` is `ELIGIBLE` after exporting. + // https://github.com/hashicorp/terraform-plugin-sdk/issues/1069 + Config: testAccCertificateConfig_privateCertificate_renewable(commonName.String(), certificateDomainName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckCertificateExists(resourceName, &v2), + testAccCheckCertificateNotRenewed(&v1, &v2), + resource.TestCheckResourceAttr(resourceName, "early_renewal_duration", ""), + resource.TestCheckResourceAttr(resourceName, "pending_renewal", "false"), + resource.TestCheckResourceAttr(resourceName, "renewal_eligibility", acm.RenewalEligibilityEligible), + resource.TestCheckResourceAttr(resourceName, "renewal_summary.#", "0"), + resource.TestCheckResourceAttr(resourceName, "status", acm.CertificateStatusIssued), + resource.TestCheckResourceAttr(resourceName, "type", acm.CertificateTypePrivate), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"early_renewal_duration"}, + }, + }, + }) } // TestAccACMCertificate_Root_trailingPeriod updated in 3.0 to account for domain_name plan-time validation @@ -1165,6 +1503,26 @@ func testAccCheckCertficateNotRecreated(v1, v2 *acm.CertificateDetail) resource. } } +func testAccCheckCertificateRenewed(i, j *acm.CertificateDetail) resource.TestCheckFunc { + return func(s *terraform.State) error { + if !aws.TimeValue(j.NotAfter).After(aws.TimeValue(i.NotAfter)) { + return fmt.Errorf("ACM Certificate not renewed: i.NotAfter=%q, j.NotAfter=%q", aws.TimeValue(i.NotAfter), aws.TimeValue(j.NotAfter)) + } + + return nil + } +} + +func testAccCheckCertificateNotRenewed(i, j *acm.CertificateDetail) resource.TestCheckFunc { + return func(s *terraform.State) error { + if !aws.TimeValue(j.NotAfter).Equal(aws.TimeValue(i.NotAfter)) { + return fmt.Errorf("ACM Certificate renewed: i.NotAfter=%q, j.NotAfter=%q", aws.TimeValue(i.NotAfter), aws.TimeValue(j.NotAfter)) + } + + return nil + } +} + func testAccCertificateConfig_basic(domainName, validationMethod string) string { return fmt.Sprintf(` resource "aws_acm_certificate" "test" { From 78eec11d73aff4895adbb9840ae26abad31fb466 Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Wed, 28 Sep 2022 13:39:33 -0700 Subject: [PATCH 25/26] Updates `docscheck` make rule to be consistent with GitHub workflow. Works around rule in ACM Certificate documentation --- .changelog/26784.txt | 2 +- GNUmakefile | 5 ++++- website/docs/r/acm_certificate.html.markdown | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.changelog/26784.txt b/.changelog/26784.txt index 7fcc6fa3aa2..604092ef313 100644 --- a/.changelog/26784.txt +++ b/.changelog/26784.txt @@ -1,3 +1,3 @@ ```release-note:enhancement resource/aws_acm_certificate: Add `early_renewal_duration`, `pending_renewal`, `renewal_eligibility`, `renewal_summary` and `type` attributes -``` \ No newline at end of file +``` diff --git a/GNUmakefile b/GNUmakefile index 5603aabef42..42501179bb8 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -139,7 +139,10 @@ docs-lint-fix: docscheck: @tfproviderdocs check \ -allowed-resource-subcategories-file website/allowed-subcategories.txt \ - -ignore-side-navigation-data-sources aws_alb,aws_alb_listener,aws_alb_target_group,aws_kms_secret \ + -enable-contents-check \ + -ignore-file-missing-data-sources aws_alb,aws_alb_listener,aws_alb_target_group \ + -ignore-file-missing-resources aws_alb,aws_alb_listener,aws_alb_listener_certificate,aws_alb_listener_rule,aws_alb_target_group,aws_alb_target_group_attachment \ + -provider-name=aws \ -require-resource-subcategory @misspell -error -source text CHANGELOG.md .changelog diff --git a/website/docs/r/acm_certificate.html.markdown b/website/docs/r/acm_certificate.html.markdown index 476a8268b3c..bbaf61c82d7 100644 --- a/website/docs/r/acm_certificate.html.markdown +++ b/website/docs/r/acm_certificate.html.markdown @@ -33,7 +33,7 @@ Domain validation through email is also supported but should be avoided as it re It's recommended to specify `create_before_destroy = true` in a [lifecycle][1] block to replace a certificate which is currently in use (eg, by [`aws_lb_listener`](lb_listener.html)). -## Imported Certificates +## Certificates Imported from Other Certificate Authority Imported certificates can be used to make certificates created with an external certificate authority available for AWS services. From e9c97ec3fe7bd557111bd67cbd7f7dd89e220a8a Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Wed, 28 Sep 2022 14:31:21 -0700 Subject: [PATCH 26/26] Updates URLs for ACM PCA reference documentation --- internal/service/acmpca/certificate_authority.go | 10 +++++----- .../acmpca/certificate_authority_data_source.go | 6 +++--- website/docs/r/acmpca_certificate.html.markdown | 5 +++-- .../docs/r/acmpca_certificate_authority.html.markdown | 4 ++-- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/internal/service/acmpca/certificate_authority.go b/internal/service/acmpca/certificate_authority.go index dfe2f0fbd31..a81323f5f28 100644 --- a/internal/service/acmpca/certificate_authority.go +++ b/internal/service/acmpca/certificate_authority.go @@ -55,7 +55,7 @@ func ResourceCertificateAuthority() *schema.Resource { Type: schema.TypeString, Computed: true, }, - // https://docs.aws.amazon.com/acm-pca/latest/APIReference/API_CertificateAuthorityConfiguration.html + // https://docs.aws.amazon.com/privateca/latest/APIReference/API_CertificateAuthorityConfiguration.html "certificate_authority_configuration": { Type: schema.TypeList, Required: true, @@ -86,7 +86,7 @@ func ResourceCertificateAuthority() *schema.Resource { acmpca.SigningAlgorithmSha512withrsa, }, false), }, - // https://docs.aws.amazon.com/acm-pca/latest/APIReference/API_ASN1Subject.html + // https://docs.aws.amazon.com/privateca/latest/APIReference/API_ASN1Subject.html "subject": { Type: schema.TypeList, Required: true, @@ -199,7 +199,7 @@ func ResourceCertificateAuthority() *schema.Resource { Type: schema.TypeString, Computed: true, }, - // https://docs.aws.amazon.com/acm-pca/latest/APIReference/API_RevocationConfiguration.html + // https://docs.aws.amazon.com/privateca/latest/APIReference/API_RevocationConfiguration.html "revocation_configuration": { Type: schema.TypeList, Optional: true, @@ -212,7 +212,7 @@ func ResourceCertificateAuthority() *schema.Resource { }, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - // https://docs.aws.amazon.com/acm-pca/latest/APIReference/API_CrlConfiguration.html + // https://docs.aws.amazon.com/privateca/latest/APIReference/API_CrlConfiguration.html "crl_configuration": { Type: schema.TypeList, Optional: true, @@ -255,7 +255,7 @@ func ResourceCertificateAuthority() *schema.Resource { }, }, }, - // https://docs.aws.amazon.com/acm-pca/latest/APIReference/API_OcspConfiguration.html + // https://docs.aws.amazon.com/privateca/latest/APIReference/API_OcspConfiguration.html "ocsp_configuration": { Type: schema.TypeList, Optional: true, diff --git a/internal/service/acmpca/certificate_authority_data_source.go b/internal/service/acmpca/certificate_authority_data_source.go index ccacc4f586b..df30ac83ba1 100644 --- a/internal/service/acmpca/certificate_authority_data_source.go +++ b/internal/service/acmpca/certificate_authority_data_source.go @@ -42,14 +42,14 @@ func DataSourceCertificateAuthority() *schema.Resource { Type: schema.TypeString, Computed: true, }, - // https://docs.aws.amazon.com/acm-pca/latest/APIReference/API_RevocationConfiguration.html + // https://docs.aws.amazon.com/privateca/latest/APIReference/API_RevocationConfiguration.html "revocation_configuration": { Type: schema.TypeList, Optional: true, Computed: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - // https://docs.aws.amazon.com/acm-pca/latest/APIReference/API_CrlConfiguration.html + // https://docs.aws.amazon.com/privateca/latest/APIReference/API_CrlConfiguration.html "crl_configuration": { Type: schema.TypeList, Optional: true, @@ -79,7 +79,7 @@ func DataSourceCertificateAuthority() *schema.Resource { }, }, }, - // https://docs.aws.amazon.com/acm-pca/latest/APIReference/API_OcspConfiguration.html + // https://docs.aws.amazon.com/privateca/latest/APIReference/API_OcspConfiguration.html "ocsp_configuration": { Type: schema.TypeList, Optional: true, diff --git a/website/docs/r/acmpca_certificate.html.markdown b/website/docs/r/acmpca_certificate.html.markdown index 50d54adf795..cd74cdab1f6 100644 --- a/website/docs/r/acmpca_certificate.html.markdown +++ b/website/docs/r/acmpca_certificate.html.markdown @@ -63,9 +63,10 @@ The following arguments are supported: * `certificate_authority_arn` - (Required) ARN of the certificate authority. * `certificate_signing_request` - (Required) Certificate Signing Request in PEM format. -* `signing_algorithm` - (Required) Algorithm to use to sign certificate requests. Valid values: `SHA256WITHRSA`, `SHA256WITHECDSA`, `SHA384WITHRSA`, `SHA384WITHECDSA`, `SHA512WITHRSA`, `SHA512WITHECDSA` +* `signing_algorithm` - (Required) Algorithm to use to sign certificate requests. Valid values: `SHA256WITHRSA`, `SHA256WITHECDSA`, `SHA384WITHRSA`, `SHA384WITHECDSA`, `SHA512WITHRSA`, `SHA512WITHECDSA`. * `validity` - (Required) Configures end of the validity period for the certificate. See [validity block](#validity-block) below. -* `template_arn` - (Optional) Template to use when issuing a certificate. See [ACM PCA Documentation](https://docs.aws.amazon.com/acm-pca/latest/userguide/UsingTemplates.html) for more information. +* `template_arn` - (Optional) Template to use when issuing a certificate. + See [ACM PCA Documentation](https://docs.aws.amazon.com/privateca/latest/userguide/UsingTemplates.html) for more information. ### validity block diff --git a/website/docs/r/acmpca_certificate_authority.html.markdown b/website/docs/r/acmpca_certificate_authority.html.markdown index 3888b5438c6..f00d92cae6f 100644 --- a/website/docs/r/acmpca_certificate_authority.html.markdown +++ b/website/docs/r/acmpca_certificate_authority.html.markdown @@ -100,8 +100,8 @@ The following arguments are supported: ### certificate_authority_configuration -* `key_algorithm` - (Required) Type of the public key algorithm and size, in bits, of the key pair that your key pair creates when it issues a certificate. Valid values can be found in the [ACM PCA Documentation](https://docs.aws.amazon.com/acm-pca/latest/APIReference/API_CertificateAuthorityConfiguration.html). -* `signing_algorithm` - (Required) Name of the algorithm your private CA uses to sign certificate requests. Valid values can be found in the [ACM PCA Documentation](https://docs.aws.amazon.com/acm-pca/latest/APIReference/API_CertificateAuthorityConfiguration.html). +* `key_algorithm` - (Required) Type of the public key algorithm and size, in bits, of the key pair that your key pair creates when it issues a certificate. Valid values can be found in the [ACM PCA Documentation](https://docs.aws.amazon.com/privateca/latest/APIReference/API_CertificateAuthorityConfiguration.html). +* `signing_algorithm` - (Required) Name of the algorithm your private CA uses to sign certificate requests. Valid values can be found in the [ACM PCA Documentation](https://docs.aws.amazon.com/privateca/latest/APIReference/API_CertificateAuthorityConfiguration.html). * `subject` - (Required) Nested argument that contains X.500 distinguished name information. At least one nested attribute must be specified. #### subject