From 6807a0b554af349ee9b76d433fef5710d65368ff Mon Sep 17 00:00:00 2001 From: David Liao Date: Mon, 23 Sep 2019 16:17:54 -0700 Subject: [PATCH 01/31] private cert issuing --- ...resource_aws_acmpca_private_certificate.go | 206 ++++++++++++++++++ 1 file changed, 206 insertions(+) create mode 100644 aws/resource_aws_acmpca_private_certificate.go diff --git a/aws/resource_aws_acmpca_private_certificate.go b/aws/resource_aws_acmpca_private_certificate.go new file mode 100644 index 000000000000..503c749f91db --- /dev/null +++ b/aws/resource_aws_acmpca_private_certificate.go @@ -0,0 +1,206 @@ +package aws + +import ( + "crypto/x509" + "encoding/pem" + "fmt" + "log" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/acmpca" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/helper/validation" +) + +func resourceAwsAcmpcaPrivateCertificate() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsAcmpcaPrivateCertificateCreate, + Read: resourceAwsAcmpcaPrivateCertificateRead, + Delete: resourceAwsAcmpcaPrivateCertificateRevoke, + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(5 * time.Minute), + }, + SchemaVersion: 1, + + Schema: map[string]*schema.Schema{ + "certificate_arn": { + Type: schema.TypeString, + Computed: true, + }, + "certificate": { + Type: schema.TypeString, + Computed: true, + StateFunc: normalizeCert, + }, + "certificate_chain": { + Type: schema.TypeString, + Computed: true, + StateFunc: normalizeCert, + }, + "certificate_authority_arn": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "certificate_signing_request": { + Type: schema.TypeString, + Required: true, + StateFunc: normalizeCert, + }, + "signing_algorithm": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{ + acmpca.SigningAlgorithmSha256withecdsa, + acmpca.SigningAlgorithmSha256withrsa, + acmpca.SigningAlgorithmSha384withecdsa, + acmpca.SigningAlgorithmSha384withrsa, + acmpca.SigningAlgorithmSha512withecdsa, + acmpca.SigningAlgorithmSha512withrsa, + }, false), + }, + "validity_length": { + Type: schema.TypeInt, + Required: true, + ForceNew: true, + }, + "validity_unit": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{ + acmpca.ValidityPeriodTypeAbsolute, + acmpca.ValidityPeriodTypeDays, + acmpca.ValidityPeriodTypeEndDate, + acmpca.ValidityPeriodTypeMonths, + acmpca.ValidityPeriodTypeYears, + }, false), + }, + "template_arn": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{ + "arn:aws:acm-pca:::template/EndEntityCertificate/V1", + "arn:aws:acm-pca:::template/SubordinateCACertificate_PathLen0/V1", + "arn:aws:acm-pca:::template/SubordinateCACertificate_PathLen1/V1", + "arn:aws:acm-pca:::template/SubordinateCACertificate_PathLen2/V1", + "arn:aws:acm-pca:::template/SubordinateCACertificate_PathLen3/V1", + "arn:aws:acm-pca:::template/RootCACertificate/V1", + }, false), + Default: "arn:aws:acm-pca:::template/EndEntityCertificate/V1", + }, + }, + } +} + +func resourceAwsAcmpcaPrivateCertificateCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).acmpcaconn + + input := &acmpca.IssueCertificateInput{ + CertificateAuthorityArn: aws.String(d.Get("certificate_authority_arn").(string)), + Csr: []byte(d.Get("certificate_signing_request").(string)), + IdempotencyToken: aws.String(resource.UniqueId()), + SigningAlgorithm: aws.String(d.Get("signing_algorithm").(string)), + TemplateArn: aws.String(d.Get("template_arn").(string)), + Validity: &acmpca.Validity{ + Type: aws.String(d.Get("validity_unit").(string)), + Value: aws.Int64(d.Get("validity_length").(int64)), + }, + } + + log.Printf("[DEBUG] ACMPCA Issue Certificate: %s", input) + var output *acmpca.IssueCertificateOutput + err := resource.Retry(1*time.Minute, func() *resource.RetryError { + var err error + output, err = conn.IssueCertificate(input) + if err != nil { + return resource.NonRetryableError(err) + } + return nil + }) + if isResourceTimeoutError(err) { + output, err = conn.IssueCertificate(input) + } + if err != nil { + return fmt.Errorf("error issuing ACMPCA Certificate: %s", err) + } + + d.SetId(aws.StringValue(output.CertificateArn)) + d.Set("certificate_arn", aws.StringValue(output.CertificateArn)) + + getCertificateInput := &acmpca.GetCertificateInput{ + CertificateArn: output.CertificateArn, + CertificateAuthorityArn: aws.String(d.Get("certificate_authority_arn").(string)), + } + + err = conn.WaitUntilCertificateIssued(getCertificateInput) + if err != nil { + return fmt.Errorf("error waiting for ACMPCA to issue Certificate %q, error: %s", d.Id(), err) + } + + return resourceAwsAcmpcaPrivateCertificateRead(d, meta) +} + +func resourceAwsAcmpcaPrivateCertificateRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).acmpcaconn + + getCertificateInput := &acmpca.GetCertificateInput{ + CertificateArn: aws.String(d.Id()), + CertificateAuthorityArn: aws.String(d.Get("certificate_authority_arn").(string)), + } + + log.Printf("[DEBUG] Reading ACMPCA Certificate: %s", getCertificateInput) + + certificateOutput, err := conn.GetCertificate(getCertificateInput) + if err != nil { + if isAWSErr(err, acmpca.ErrCodeResourceNotFoundException, "") { + log.Printf("[WARN] ACMPCA Certificate %q not found - removing from state", d.Id()) + d.SetId("") + return nil + } + return fmt.Errorf("error reading ACMPCA Certificate: %s", err) + } + + d.Set("certificate_arn", d.Id()) + d.Set("certificate", aws.StringValue(certificateOutput.Certificate)) + d.Set("certificate_chain", aws.StringValue(certificateOutput.CertificateChain)) + + return nil +} + +func resourceAwsAcmpcaPrivateCertificateRevoke(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).acmpcaconn + + block, _ := pem.Decode([]byte(d.Get("certificate").(string))) + if block == nil { + log.Printf("[WARN] Failed to parse certificate %q", d.Id()) + return nil + } + cert, err := x509.ParseCertificate(block.Bytes) + if err != nil { + fmt.Errorf("Failed to parse ACMPCA Certificate: %s", err) + } + + input := &acmpca.RevokeCertificateInput{ + CertificateAuthorityArn: aws.String(d.Get("certificate_authority_arn").(string)), + CertificateSerial: aws.String(fmt.Sprintf("%x", cert.SerialNumber)), + RevocationReason: aws.String("Terraform Delete"), + } + + log.Printf("[DEBUG] Revoking ACMPCA Certificate: %s", input) + _, err = conn.RevokeCertificate(input) + if err != nil { + if isAWSErr(err, acmpca.ErrCodeResourceNotFoundException, "") || + isAWSErr(err, acmpca.ErrCodeRequestAlreadyProcessedException, "") || + isAWSErr(err, acmpca.ErrCodeRequestInProgressException, "") { + return nil + } + return fmt.Errorf("error revoking ACMPCA Certificate: %s", err) + } + + return nil +} From b88a02ccf1b510701fc36ad5ac041984fc758c38 Mon Sep 17 00:00:00 2001 From: David Liao Date: Mon, 23 Sep 2019 17:49:38 -0700 Subject: [PATCH 02/31] tests --- CHANGELOG.md | 6 + aws/provider.go | 1 + ...resource_aws_acmpca_private_certificate.go | 20 ++-- ...rce_aws_acmpca_private_certificate_test.go | 109 ++++++++++++++++++ 4 files changed, 125 insertions(+), 11 deletions(-) create mode 100644 aws/resource_aws_acmpca_private_certificate_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index cda78abb8ae2..7e1f5e29b12c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,10 @@ ## 2.30.0 (Unreleased) + +FEATURES: + +* **New Data Source:** `aws_acmpca_private_certificate` ([#10183](https://github.com/terraform-providers/terraform-provider-aws/issues/10183)) +* **New Resource:** `aws_acmpca_private_certificate` ([#10183](https://github.com/terraform-providers/terraform-provider-aws/issues/10183)) +* ## 2.29.0 (September 20, 2019) ENHANCEMENTS: diff --git a/aws/provider.go b/aws/provider.go index 837de3ae8513..26652a770967 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -298,6 +298,7 @@ func Provider() terraform.ResourceProvider { "aws_acm_certificate": resourceAwsAcmCertificate(), "aws_acm_certificate_validation": resourceAwsAcmCertificateValidation(), "aws_acmpca_certificate_authority": resourceAwsAcmpcaCertificateAuthority(), + "aws_acmpca_private_certificate": resourceAwsAcmpcaPrivateCertificate(), "aws_ami": resourceAwsAmi(), "aws_ami_copy": resourceAwsAmiCopy(), "aws_ami_from_instance": resourceAwsAmiFromInstance(), diff --git a/aws/resource_aws_acmpca_private_certificate.go b/aws/resource_aws_acmpca_private_certificate.go index 503c749f91db..02dcfa36b37a 100644 --- a/aws/resource_aws_acmpca_private_certificate.go +++ b/aws/resource_aws_acmpca_private_certificate.go @@ -25,19 +25,17 @@ func resourceAwsAcmpcaPrivateCertificate() *schema.Resource { SchemaVersion: 1, Schema: map[string]*schema.Schema{ - "certificate_arn": { + "arn": { Type: schema.TypeString, Computed: true, }, "certificate": { - Type: schema.TypeString, - Computed: true, - StateFunc: normalizeCert, + Type: schema.TypeString, + Computed: true, }, "certificate_chain": { - Type: schema.TypeString, - Computed: true, - StateFunc: normalizeCert, + Type: schema.TypeString, + Computed: true, }, "certificate_authority_arn": { Type: schema.TypeString, @@ -47,6 +45,7 @@ func resourceAwsAcmpcaPrivateCertificate() *schema.Resource { "certificate_signing_request": { Type: schema.TypeString, Required: true, + ForceNew: true, StateFunc: normalizeCert, }, "signing_algorithm": { @@ -108,7 +107,7 @@ func resourceAwsAcmpcaPrivateCertificateCreate(d *schema.ResourceData, meta inte TemplateArn: aws.String(d.Get("template_arn").(string)), Validity: &acmpca.Validity{ Type: aws.String(d.Get("validity_unit").(string)), - Value: aws.Int64(d.Get("validity_length").(int64)), + Value: aws.Int64(int64(d.Get("validity_length").(int))), }, } @@ -130,7 +129,6 @@ func resourceAwsAcmpcaPrivateCertificateCreate(d *schema.ResourceData, meta inte } d.SetId(aws.StringValue(output.CertificateArn)) - d.Set("certificate_arn", aws.StringValue(output.CertificateArn)) getCertificateInput := &acmpca.GetCertificateInput{ CertificateArn: output.CertificateArn, @@ -165,7 +163,7 @@ func resourceAwsAcmpcaPrivateCertificateRead(d *schema.ResourceData, meta interf return fmt.Errorf("error reading ACMPCA Certificate: %s", err) } - d.Set("certificate_arn", d.Id()) + d.Set("arn", d.Id()) d.Set("certificate", aws.StringValue(certificateOutput.Certificate)) d.Set("certificate_chain", aws.StringValue(certificateOutput.CertificateChain)) @@ -188,7 +186,7 @@ func resourceAwsAcmpcaPrivateCertificateRevoke(d *schema.ResourceData, meta inte input := &acmpca.RevokeCertificateInput{ CertificateAuthorityArn: aws.String(d.Get("certificate_authority_arn").(string)), CertificateSerial: aws.String(fmt.Sprintf("%x", cert.SerialNumber)), - RevocationReason: aws.String("Terraform Delete"), + RevocationReason: aws.String(acmpca.RevocationReasonUnspecified), } log.Printf("[DEBUG] Revoking ACMPCA Certificate: %s", input) diff --git a/aws/resource_aws_acmpca_private_certificate_test.go b/aws/resource_aws_acmpca_private_certificate_test.go new file mode 100644 index 000000000000..0110c3b62476 --- /dev/null +++ b/aws/resource_aws_acmpca_private_certificate_test.go @@ -0,0 +1,109 @@ +package aws + +import ( + "fmt" + "regexp" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/acmpca" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccAwsAcmpcaPrivateCertificate_Basic(t *testing.T) { + resourceName := "aws_acmpca_private_certificate.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProvidersWithTLS, + CheckDestroy: testAccCheckAwsAcmpcaPrivateCertificateDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsAcmpcaPrivateCertificateConfig_Required, + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsAcmpcaPrivateCertificateExists(resourceName), + resource.TestMatchResourceAttr(resourceName, "arn", regexp.MustCompile(`^arn:[^:]+:acm-pca:[^:]+:[^:]+:certificate-authority/.+/certificate/.+$`)), + resource.TestCheckResourceAttrSet(resourceName, "certificate"), + resource.TestCheckResourceAttrSet(resourceName, "certificate_chain"), + resource.TestCheckResourceAttrSet(resourceName, "certificate_signing_request"), + resource.TestCheckResourceAttr(resourceName, "validity_length", "1"), + resource.TestCheckResourceAttr(resourceName, "validity_unit", "YEARS"), + resource.TestCheckResourceAttr(resourceName, "signing_algorithm", "SHA256WITHRSA"), + resource.TestCheckResourceAttr(resourceName, "template_arn", "arn:aws:acm-pca:::template/EndEntityCertificate/V1"), + ), + }, + }, + }) +} + +func testAccCheckAwsAcmpcaPrivateCertificateDestroy(s *terraform.State) error { + // unfortunately aws pca does not have an API to determine if a cert has been revoked. + // see: https://docs.aws.amazon.com/acm-pca/latest/userguide/PcaRevokeCert.html + return nil +} + +func testAccCheckAwsAcmpcaPrivateCertificateExists(resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("Not found: %s", resourceName) + } + + conn := testAccProvider.Meta().(*AWSClient).acmpcaconn + input := &acmpca.GetCertificateInput{ + CertificateArn: aws.String(rs.Primary.ID), + CertificateAuthorityArn: aws.String(rs.Primary.Attributes["certificate_authority_arn"]), + } + + output, err := conn.GetCertificate(input) + + if err != nil { + return err + } + + if output == nil || output.Certificate == nil { + return fmt.Errorf("ACMPCA Certificate %q does not exist", rs.Primary.ID) + } + + return nil + } +} + +const testAccAwsAcmpcaPrivateCertificateConfig_Required = ` +resource "tls_private_key" "key" { + algorithm = "RSA" +} + +resource "tls_cert_request" "csr" { + key_algorithm = "RSA" + private_key_pem = tls_private_key.key.private_key_pem + + subject { + common_name = "testing" + } + +} + +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 = "terraformtesting.com" + } + } +} + +resource "aws_acmpca_private_certificate" "test" { + certificate_authority_arn = aws_acmpca_certificate_authority.test.arn + certificate_signing_request = tls_cert_request.csr.cert_request_pem + signing_algorithm = "SHA256WITHRSA" + validity_length = 1 + validity_unit = "YEARS" +} +` From 557335dc39dc7d7b7f5b108d033bc8d0183f0b03 Mon Sep 17 00:00:00 2001 From: David Liao Date: Mon, 23 Sep 2019 19:00:36 -0700 Subject: [PATCH 03/31] data resources and docs --- ...a_source_aws_acmpca_private_certificate.go | 58 ++++++++++ ...rce_aws_acmpca_private_certificate_test.go | 103 ++++++++++++++++++ aws/provider.go | 1 + ...rce_aws_acmpca_private_certificate_test.go | 1 - website/aws.erb | 2 + .../acmpca_private_certificate.html.markdown | 35 ++++++ .../acmpca_private_certificate.html.markdown | 83 ++++++++++++++ 7 files changed, 282 insertions(+), 1 deletion(-) create mode 100644 aws/data_source_aws_acmpca_private_certificate.go create mode 100644 aws/data_source_aws_acmpca_private_certificate_test.go create mode 100644 website/docs/d/acmpca_private_certificate.html.markdown create mode 100644 website/docs/r/acmpca_private_certificate.html.markdown diff --git a/aws/data_source_aws_acmpca_private_certificate.go b/aws/data_source_aws_acmpca_private_certificate.go new file mode 100644 index 000000000000..07f0c62ec497 --- /dev/null +++ b/aws/data_source_aws_acmpca_private_certificate.go @@ -0,0 +1,58 @@ +package aws + +import ( + "fmt" + "log" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/acmpca" + "github.com/hashicorp/terraform/helper/schema" +) + +func dataSourceAwsAcmpcaPrivateCertificate() *schema.Resource { + return &schema.Resource{ + Read: dataSourceAwsAcmpcaPrivateCertificateRead, + + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Required: true, + }, + "certificate_authority_arn": { + Type: schema.TypeString, + Required: true, + }, + "certificate": { + Type: schema.TypeString, + Computed: true, + }, + "certificate_chain": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func dataSourceAwsAcmpcaPrivateCertificateRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).acmpcaconn + certificateArn := d.Get("arn").(string) + + getCertificateInput := &acmpca.GetCertificateInput{ + CertificateArn: aws.String(certificateArn), + CertificateAuthorityArn: aws.String(d.Get("certificate_authority_arn").(string)), + } + + log.Printf("[DEBUG] Reading ACMPCA Certificate: %s", getCertificateInput) + + certificateOutput, err := conn.GetCertificate(getCertificateInput) + if err != nil { + return fmt.Errorf("error reading ACMPCA Certificate: %s", err) + } + + d.SetId(certificateArn) + d.Set("certificate", aws.StringValue(certificateOutput.Certificate)) + d.Set("certificate_chain", aws.StringValue(certificateOutput.CertificateChain)) + + return nil +} diff --git a/aws/data_source_aws_acmpca_private_certificate_test.go b/aws/data_source_aws_acmpca_private_certificate_test.go new file mode 100644 index 000000000000..75fd009480af --- /dev/null +++ b/aws/data_source_aws_acmpca_private_certificate_test.go @@ -0,0 +1,103 @@ +package aws + +import ( + "regexp" + "testing" + + "github.com/hashicorp/terraform/helper/resource" +) + +func TestAccDataSourceAwsAcmpcaPrivateCertificate_Basic(t *testing.T) { + resourceName := "aws_acmpca_private_certificate.test" + datasourceName := "data.aws_acmpca_private_certificate.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProvidersWithTLS, + Steps: []resource.TestStep{ + { + Config: testAccDataSourceAwsAcmpcaPrivateCertificateConfig_NonExistent, + ExpectError: regexp.MustCompile(`(AccessDeniedException|ResourceNotFoundException)`), + }, + { + Config: testAccDataSourceAwsAcmpcaPrivateCertificateConfig_ARN, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrPair(datasourceName, "arn", resourceName, "arn"), + resource.TestCheckResourceAttrPair(datasourceName, "certificate", resourceName, "certificate"), + resource.TestCheckResourceAttrPair(datasourceName, "certificate_chain", resourceName, "certificate_chain"), + resource.TestCheckResourceAttrPair(datasourceName, "certificate_authority_arn", resourceName, "certificate_authority_arn"), + ), + }, + }, + }) +} + +const testAccDataSourceAwsAcmpcaPrivateCertificateConfig_ARN = ` +resource "tls_private_key" "key_1" { + algorithm = "RSA" +} + +resource "tls_cert_request" "csr_1" { + key_algorithm = "RSA" + private_key_pem = tls_private_key.key_1.private_key_pem + + subject { + common_name = "wrong" + } +} + +resource "tls_private_key" "key_2" { + algorithm = "RSA" +} + +resource "tls_cert_request" "csr_2" { + key_algorithm = "RSA" + private_key_pem = tls_private_key.key_2.private_key_pem + + subject { + common_name = "testing" + } +} + +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 = "terraformtesting.com" + } + } +} + +resource "aws_acmpca_private_certificate" "wrong" { + certificate_authority_arn = aws_acmpca_certificate_authority.test.arn + certificate_signing_request = tls_cert_request.csr_1.cert_request_pem + signing_algorithm = "SHA256WITHRSA" + validity_length = 1 + validity_unit = "YEARS" +} + +resource "aws_acmpca_private_certificate" "test" { + certificate_authority_arn = aws_acmpca_certificate_authority.test.arn + certificate_signing_request = tls_cert_request.csr_2.cert_request_pem + signing_algorithm = "SHA256WITHRSA" + validity_length = 1 + validity_unit = "YEARS" +} + +data "aws_acmpca_private_certificate" "test" { + arn = aws_acmpca_private_certificate.test.arn + certificate_authority_arn = aws_acmpca_certificate_authority.test.arn +} +` + +const testAccDataSourceAwsAcmpcaPrivateCertificateConfig_NonExistent = ` +data "aws_acmpca_private_certificate" "test" { + arn = "arn:aws:acm-pca:us-east-1:123456789012:certificate-authority/does-not-exist/certificate/does-not-exist" + certificate_authority_arn = "arn:aws:acm-pca:us-east-1:123456789012:certificate-authority/does-not-exist" +} +` diff --git a/aws/provider.go b/aws/provider.go index 26652a770967..3d2e457c58ee 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -143,6 +143,7 @@ func Provider() terraform.ResourceProvider { DataSourcesMap: map[string]*schema.Resource{ "aws_acm_certificate": dataSourceAwsAcmCertificate(), "aws_acmpca_certificate_authority": dataSourceAwsAcmpcaCertificateAuthority(), + "aws_acmpca_private_certificate": dataSourceAwsAcmpcaPrivateCertificate(), "aws_ami": dataSourceAwsAmi(), "aws_ami_ids": dataSourceAwsAmiIds(), "aws_api_gateway_api_key": dataSourceAwsApiGatewayApiKey(), diff --git a/aws/resource_aws_acmpca_private_certificate_test.go b/aws/resource_aws_acmpca_private_certificate_test.go index 0110c3b62476..4a6cef8b5b10 100644 --- a/aws/resource_aws_acmpca_private_certificate_test.go +++ b/aws/resource_aws_acmpca_private_certificate_test.go @@ -82,7 +82,6 @@ resource "tls_cert_request" "csr" { subject { common_name = "testing" } - } resource "aws_acmpca_certificate_authority" "test" { diff --git a/website/aws.erb b/website/aws.erb index 8b880c6a1b07..91d0fa835b0c 100644 --- a/website/aws.erb +++ b/website/aws.erb @@ -87,6 +87,7 @@ @@ -95,6 +96,7 @@ diff --git a/website/docs/d/acmpca_private_certificate.html.markdown b/website/docs/d/acmpca_private_certificate.html.markdown new file mode 100644 index 000000000000..6dba5a8dcb75 --- /dev/null +++ b/website/docs/d/acmpca_private_certificate.html.markdown @@ -0,0 +1,35 @@ +--- +layout: "aws" +page_title: "AWS: aws_acmpca_private_certificate" +sidebar_current: "docs-aws-datasource-acmpca-private-certificate" +description: |- + Get information on a AWS Certificate Manager Private Certificate Issuing +--- + +# Data Source: aws_acmpca_private_certificate + +Get information on a AWS Certificate Manager Private Certificate Authority (ACM PCA Certificate Authority). + +## Example Usage + +```hcl +data "aws_acmpca_private_certificate" "example" { + arn = "arn:aws:acm-pca:us-east-1:123456789012:certificate-authority/12345678-1234-1234-1234-123456789012/certificate/1234b4a0d73e2056789bdbe77d5b1a23" + certificate_authority_arn = "arn:aws:acm-pca:us-east-1:123456789012:certificate-authority/12345678-1234-1234-1234-123456789012" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `arn` - (Required) Amazon Resource Name (ARN) of the certificate issued by the private certificate authority. +* `certificate_authority_arn` - (Required) Amazon Resource Name (ARN) of the certificate authority. + +## Attribute Reference + +In addition to all arguments above, the following attributes are exported: + +* `id` - Amazon Resource Name (ARN) of the certificate authority. +* `certificate` - Base64-encoded certificate authority (CA) certificate. Only available after the certificate authority certificate has been imported. +* `certificate_chain` - Base64-encoded certificate chain that includes any intermediate certificates and chains up to root on-premises certificate that you used to sign your private CA certificate. The chain does not include your private CA certificate. Only available after the certificate authority certificate has been imported. diff --git a/website/docs/r/acmpca_private_certificate.html.markdown b/website/docs/r/acmpca_private_certificate.html.markdown new file mode 100644 index 000000000000..fa1bb6a83508 --- /dev/null +++ b/website/docs/r/acmpca_private_certificate.html.markdown @@ -0,0 +1,83 @@ +--- +layout: "aws" +page_title: "AWS: aws_acmpca_private_certificate" +sidebar_current: "docs-aws-resource-acmpca-certificate-authority" +description: |- + Provides a resource to manage AWS Certificate Manager Private Certificate Issuing +--- + +# Resource: aws_acmpca_private_certificate + +Provides a resource to manage AWS Certificate Manager Private Certificate Issuing (ACM PCA Certificate Issuing). It is to be used when signing a certificate that was generated outside AWS and have `aws_acmpca_certificate_authority` as the CA. + +## Example Usage + +### Basic + +```hcl +resource "aws_acmpca_certificate_authority" "example" { + private_certificate_configuration { + key_algorithm = "RSA_4096" + signing_algorithm = "SHA512WITHRSA" + + subject { + common_name = "example.com" + } + } + + permanent_deletion_time_in_days = 7 +} + +resource "tls_private_key" "key" { + algorithm = "RSA" +} + +resource "tls_cert_request" "csr" { + key_algorithm = "RSA" + private_key_pem = tls_private_key.key.private_key_pem + + subject { + common_name = "example" + } +} + +resource "aws_acmpca_private_certificate" "example" { + certificate_authority_arn = aws_acmpca_certificate_authority.example.arn + certificate_signing_request = tls_cert_request.csr.cert_request_pem + signing_algorithm = "SHA256WITHRSA" + validity_length = 1 + validity_unit = "YEARS" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `certificate_authority_arn` - (Required) Amazon Resource Name (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` +* `validity_length` - (Required) Used with `validity_unit` as the number to apply with the unit +* `validity_unit` - (Required) The unit of time for certificate validity. Cannot be set to a value higher than the validity of the Certficate Authority. Valid values: `DAYS`, `MONTHS`, `YEARS`, `ABSOLUTE`, `END_DATE`. +* `template_arn` - (Optional) The template to use when issueing a certificate. See [ACM PCA Documentation](https://docs.aws.amazon.com/acm-pca/latest/userguide/UsingTemplates.html) for more information. + + +## Attribute Reference + +In addition to all arguments above, the following attributes are exported: + +* `id` - Amazon Resource Name (ARN) of the certificate. +* `arn` - Amazon Resource Name (ARN) of the certificate. +* `certificate` - Certificate PEM. +* `certificate_chain` - Certificate chain that includes any intermediate certificates and chains up to root CA that you used to sign your private certificate. + +## Timeouts + +`aws_acmpca_private_certificate` provides the following [Timeouts](/docs/configuration/resources.html#timeouts) +configuration options: + +* `create` - (Default `5m`) How long to wait for a certificate authority to issue a certificate. + +## Import + +`aws_acmpca_private_certificate` can not be imported at this time. From f5f1fadcf04011771b5bd907eba75c9959c04143 Mon Sep 17 00:00:00 2001 From: David Liao Date: Mon, 23 Sep 2019 19:03:31 -0700 Subject: [PATCH 04/31] minor --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e1f5e29b12c..926af9d8d4e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,8 @@ FEATURES: * **New Data Source:** `aws_acmpca_private_certificate` ([#10183](https://github.com/terraform-providers/terraform-provider-aws/issues/10183)) * **New Resource:** `aws_acmpca_private_certificate` ([#10183](https://github.com/terraform-providers/terraform-provider-aws/issues/10183)) -* + + ## 2.29.0 (September 20, 2019) ENHANCEMENTS: From 142d8beb9dd135f414fa813e00871749fcc61914 Mon Sep 17 00:00:00 2001 From: David Liao Date: Mon, 23 Sep 2019 19:35:55 -0700 Subject: [PATCH 05/31] typos --- aws/resource_aws_acmpca_private_certificate.go | 2 +- website/docs/r/acmpca_private_certificate.html.markdown | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/aws/resource_aws_acmpca_private_certificate.go b/aws/resource_aws_acmpca_private_certificate.go index 02dcfa36b37a..e36462b25f08 100644 --- a/aws/resource_aws_acmpca_private_certificate.go +++ b/aws/resource_aws_acmpca_private_certificate.go @@ -180,7 +180,7 @@ func resourceAwsAcmpcaPrivateCertificateRevoke(d *schema.ResourceData, meta inte } cert, err := x509.ParseCertificate(block.Bytes) if err != nil { - fmt.Errorf("Failed to parse ACMPCA Certificate: %s", err) + return fmt.Errorf("Failed to parse ACMPCA Certificate: %s", err) } input := &acmpca.RevokeCertificateInput{ diff --git a/website/docs/r/acmpca_private_certificate.html.markdown b/website/docs/r/acmpca_private_certificate.html.markdown index fa1bb6a83508..5537e4670f27 100644 --- a/website/docs/r/acmpca_private_certificate.html.markdown +++ b/website/docs/r/acmpca_private_certificate.html.markdown @@ -59,7 +59,7 @@ The following arguments are supported: * `signing_algorithm` - (Required) Algorithm to use to sign certificate requests. Valid values: `SHA256WITHRSA`, `SHA256WITHECDSA`, `SHA384WITHRSA`, `SHA384WITHECDSA`, `SHA512WITHRSA`, `SHA512WITHECDSA` * `validity_length` - (Required) Used with `validity_unit` as the number to apply with the unit * `validity_unit` - (Required) The unit of time for certificate validity. Cannot be set to a value higher than the validity of the Certficate Authority. Valid values: `DAYS`, `MONTHS`, `YEARS`, `ABSOLUTE`, `END_DATE`. -* `template_arn` - (Optional) The template to use when issueing a certificate. See [ACM PCA Documentation](https://docs.aws.amazon.com/acm-pca/latest/userguide/UsingTemplates.html) for more information. +* `template_arn` - (Optional) The 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. ## Attribute Reference From 74f97877b44b00b7ef50f5787018633033acb51a Mon Sep 17 00:00:00 2001 From: David Liao Date: Thu, 26 Sep 2019 16:21:35 -0700 Subject: [PATCH 06/31] update changelog --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 15d44583e7cd..b46517134f58 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,12 @@ ## 2.31.0 (Unreleased) -## 2.30.0 (September 26, 2019) FEATURES: * **New Data Source:** `aws_acmpca_private_certificate` ([#10183](https://github.com/terraform-providers/terraform-provider-aws/issues/10183)) * **New Resource:** `aws_acmpca_private_certificate` ([#10183](https://github.com/terraform-providers/terraform-provider-aws/issues/10183)) +## 2.30.0 (September 26, 2019) + NOTES: * provider: The default development, testing, and building of the Terraform AWS Provider binary is now done with Go 1.13. This version of Go now requires macOS 10.11 El Capitan or later and FreeBSD 11.2 or later. Support for previous versions of those operating systems has been discontinued. ([#10206](https://github.com/terraform-providers/terraform-provider-aws/issues/10206)) From eee3a9750ec8b8b065cbf83faa7a6cbbccb1cb5c Mon Sep 17 00:00:00 2001 From: David Liao Date: Fri, 4 Oct 2019 11:00:30 -0700 Subject: [PATCH 07/31] fix conflicts --- CHANGELOG.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 293f69406b93..6afd9ae7a02f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,15 +1,16 @@ ## 2.32.0 (Unreleased) -## 2.31.0 (October 03, 2019) - -NOTES: - -* resource/aws_lambda_function: Environments using Lambda functions with VPC configurations should upgrade their Terraform AWS Provider to this version or later to appropriately handle the networking changes introduced by the [improved VPC networking for AWS Lambda functions](https://aws.amazon.com/blogs/compute/announcing-improved-vpc-networking-for-aws-lambda-functions/) deployment. These changes prevent proper deletion of EC2 Subnets and Security Groups for accounts and regions updated to the new Lambda networking infrastructure in older versions of the Terraform AWS Provider. Additional information and configuration workarounds for prior versions can be found in [this GitHub issue](https://github.com/terraform-providers/terraform-provider-aws/issues/10329). FEATURES: * **New Data Source:** `aws_acmpca_private_certificate` ([#10183](https://github.com/terraform-providers/terraform-provider-aws/issues/10183)) * **New Resource:** `aws_acmpca_private_certificate` ([#10183](https://github.com/terraform-providers/terraform-provider-aws/issues/10183)) +## 2.31.0 (October 03, 2019) + +NOTES: + +* resource/aws_lambda_function: Environments using Lambda functions with VPC configurations should upgrade their Terraform AWS Provider to this version or later to appropriately handle the networking changes introduced by the [improved VPC networking for AWS Lambda functions](https://aws.amazon.com/blogs/compute/announcing-improved-vpc-networking-for-aws-lambda-functions/) deployment. These changes prevent proper deletion of EC2 Subnets and Security Groups for accounts and regions updated to the new Lambda networking infrastructure in older versions of the Terraform AWS Provider. Additional information and configuration workarounds for prior versions can be found in [this GitHub issue](https://github.com/terraform-providers/terraform-provider-aws/issues/10329). + ENHANCEMENTS: * data-source/aws_eks_cluster: Add `tags` attribute ([#10307](https://github.com/terraform-providers/terraform-provider-aws/issues/10307)) From f682fb0268cafe8b545330dd9de0fe246e73941b Mon Sep 17 00:00:00 2001 From: David Liao Date: Fri, 8 Nov 2019 11:51:18 -0800 Subject: [PATCH 08/31] update changelog --- CHANGELOG.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ce3fa8a863fb..5312eb0f1831 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ ## 2.36.0 (Unreleased) +FEATURES: + +* **New Data Source:** `aws_acmpca_private_certificate` ([#10183](https://github.com/terraform-providers/terraform-provider-aws/issues/10183)) +* **New Resource:** `aws_acmpca_private_certificate` ([#10183](https://github.com/terraform-providers/terraform-provider-aws/issues/10183)) + ENHANCEMENTS: * data-source/aws_iam_group: Add `users` attribute [GH-7132] @@ -101,11 +106,6 @@ ENHANCEMENTS: * resource/aws_emr_instance_group: Add `configurations_json` argument ([#10426](https://github.com/terraform-providers/terraform-provider-aws/issues/10426)) -FEATURES: - -* **New Data Source:** `aws_acmpca_private_certificate` ([#10183](https://github.com/terraform-providers/terraform-provider-aws/issues/10183)) -* **New Resource:** `aws_acmpca_private_certificate` ([#10183](https://github.com/terraform-providers/terraform-provider-aws/issues/10183)) - BUG FIXES: * provider: Fix session handling to correctly validate and use assume_role credentials ([#10379](https://github.com/terraform-providers/terraform-provider-aws/issues/10379)) From b4d2f418a19c5858ac3595dc28c7970edcb21db2 Mon Sep 17 00:00:00 2001 From: David Liao Date: Sun, 3 May 2020 12:01:36 -0700 Subject: [PATCH 09/31] pulling in master --- CHANGELOG.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c1e03132d09f..09785eb1affa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +FEATURES: + +* **New Data Source:** `aws_acmpca_private_certificate` ([#10183](https://github.com/terraform-providers/terraform-provider-aws/issues/10183)) +* **New Resource:** `aws_acmpca_private_certificate` ([#10183](https://github.com/terraform-providers/terraform-provider-aws/issues/10183)) + ## 2.61.0 (Unreleased) FEATURES: @@ -819,11 +824,6 @@ BUG FIXES: ## 2.36.0 (November 14, 2019) -FEATURES: - -* **New Data Source:** `aws_acmpca_private_certificate` ([#10183](https://github.com/terraform-providers/terraform-provider-aws/issues/10183)) -* **New Resource:** `aws_acmpca_private_certificate` ([#10183](https://github.com/terraform-providers/terraform-provider-aws/issues/10183)) - ENHANCEMENTS: * data-source/aws_iam_group: Add `users` attribute ([#7132](https://github.com/terraform-providers/terraform-provider-aws/issues/7132)) From 47304865b78a7a3bd5dc442e8fb48f246fd775b2 Mon Sep 17 00:00:00 2001 From: David Liao Date: Sun, 3 May 2020 12:04:40 -0700 Subject: [PATCH 10/31] no changelog --- CHANGELOG.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 09785eb1affa..a935eb512469 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,3 @@ -FEATURES: - -* **New Data Source:** `aws_acmpca_private_certificate` ([#10183](https://github.com/terraform-providers/terraform-provider-aws/issues/10183)) -* **New Resource:** `aws_acmpca_private_certificate` ([#10183](https://github.com/terraform-providers/terraform-provider-aws/issues/10183)) - ## 2.61.0 (Unreleased) FEATURES: From 17007838c4e62ea6c3b9ce313b6d5d9f2e2dfab1 Mon Sep 17 00:00:00 2001 From: David Liao Date: Sun, 3 May 2020 14:51:40 -0700 Subject: [PATCH 11/31] fix imports, docs subcat --- aws/data_source_aws_acmpca_private_certificate.go | 2 +- aws/data_source_aws_acmpca_private_certificate_test.go | 2 +- aws/resource_aws_acmpca_private_certificate.go | 6 +++--- aws/resource_aws_acmpca_private_certificate_test.go | 4 ++-- website/docs/d/acmpca_private_certificate.html.markdown | 2 +- website/docs/r/acmpca_private_certificate.html.markdown | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/aws/data_source_aws_acmpca_private_certificate.go b/aws/data_source_aws_acmpca_private_certificate.go index 07f0c62ec497..7443a2a42d7b 100644 --- a/aws/data_source_aws_acmpca_private_certificate.go +++ b/aws/data_source_aws_acmpca_private_certificate.go @@ -6,7 +6,7 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/acmpca" - "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" ) func dataSourceAwsAcmpcaPrivateCertificate() *schema.Resource { diff --git a/aws/data_source_aws_acmpca_private_certificate_test.go b/aws/data_source_aws_acmpca_private_certificate_test.go index 75fd009480af..dfab20fa9856 100644 --- a/aws/data_source_aws_acmpca_private_certificate_test.go +++ b/aws/data_source_aws_acmpca_private_certificate_test.go @@ -4,7 +4,7 @@ import ( "regexp" "testing" - "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" ) func TestAccDataSourceAwsAcmpcaPrivateCertificate_Basic(t *testing.T) { diff --git a/aws/resource_aws_acmpca_private_certificate.go b/aws/resource_aws_acmpca_private_certificate.go index e36462b25f08..aaf30c5e16b0 100644 --- a/aws/resource_aws_acmpca_private_certificate.go +++ b/aws/resource_aws_acmpca_private_certificate.go @@ -9,9 +9,9 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/acmpca" - "github.com/hashicorp/terraform/helper/resource" - "github.com/hashicorp/terraform/helper/schema" - "github.com/hashicorp/terraform/helper/validation" + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/helper/validation" ) func resourceAwsAcmpcaPrivateCertificate() *schema.Resource { diff --git a/aws/resource_aws_acmpca_private_certificate_test.go b/aws/resource_aws_acmpca_private_certificate_test.go index 4a6cef8b5b10..7467882aede3 100644 --- a/aws/resource_aws_acmpca_private_certificate_test.go +++ b/aws/resource_aws_acmpca_private_certificate_test.go @@ -7,8 +7,8 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/acmpca" - "github.com/hashicorp/terraform/helper/resource" - "github.com/hashicorp/terraform/terraform" + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/terraform" ) func TestAccAwsAcmpcaPrivateCertificate_Basic(t *testing.T) { diff --git a/website/docs/d/acmpca_private_certificate.html.markdown b/website/docs/d/acmpca_private_certificate.html.markdown index 6dba5a8dcb75..1f2f9ab760ca 100644 --- a/website/docs/d/acmpca_private_certificate.html.markdown +++ b/website/docs/d/acmpca_private_certificate.html.markdown @@ -1,7 +1,7 @@ --- +subcategory: "ACM PCA" layout: "aws" page_title: "AWS: aws_acmpca_private_certificate" -sidebar_current: "docs-aws-datasource-acmpca-private-certificate" description: |- Get information on a AWS Certificate Manager Private Certificate Issuing --- diff --git a/website/docs/r/acmpca_private_certificate.html.markdown b/website/docs/r/acmpca_private_certificate.html.markdown index 5537e4670f27..36ed13bfb489 100644 --- a/website/docs/r/acmpca_private_certificate.html.markdown +++ b/website/docs/r/acmpca_private_certificate.html.markdown @@ -1,7 +1,7 @@ --- +subcategory: "ACM PCA" layout: "aws" page_title: "AWS: aws_acmpca_private_certificate" -sidebar_current: "docs-aws-resource-acmpca-certificate-authority" description: |- Provides a resource to manage AWS Certificate Manager Private Certificate Issuing --- From 9a3be90e5f68601dcbfd485b374e419a9ddac46e Mon Sep 17 00:00:00 2001 From: David Liao Date: Sun, 3 May 2020 15:49:56 -0700 Subject: [PATCH 12/31] update tests to not use tls provider --- ...rce_aws_acmpca_private_certificate_test.go | 42 +++++-------------- ...rce_aws_acmpca_private_certificate_test.go | 25 ++++------- aws/tls.go | 39 +++++++++++++++-- aws/tls_test.go | 12 ++++++ .../acmpca_private_certificate.html.markdown | 2 +- .../acmpca_private_certificate.html.markdown | 10 ++--- .../waf_sql_injection_match_set.html.markdown | 2 +- 7 files changed, 72 insertions(+), 60 deletions(-) diff --git a/aws/data_source_aws_acmpca_private_certificate_test.go b/aws/data_source_aws_acmpca_private_certificate_test.go index dfab20fa9856..0f26d3f8fd0b 100644 --- a/aws/data_source_aws_acmpca_private_certificate_test.go +++ b/aws/data_source_aws_acmpca_private_certificate_test.go @@ -1,6 +1,7 @@ package aws import ( + "fmt" "regexp" "testing" @@ -10,17 +11,19 @@ import ( func TestAccDataSourceAwsAcmpcaPrivateCertificate_Basic(t *testing.T) { resourceName := "aws_acmpca_private_certificate.test" datasourceName := "data.aws_acmpca_private_certificate.test" + csr1, _ := tlsRsaX509CertificateRequestPem(4096, "terraformtest1.com") + csr2, _ := tlsRsaX509CertificateRequestPem(4096, "terraformtest2.com") resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProvidersWithTLS, + Providers: testAccProviders, Steps: []resource.TestStep{ { Config: testAccDataSourceAwsAcmpcaPrivateCertificateConfig_NonExistent, ExpectError: regexp.MustCompile(`(AccessDeniedException|ResourceNotFoundException)`), }, { - Config: testAccDataSourceAwsAcmpcaPrivateCertificateConfig_ARN, + Config: testAccDataSourceAwsAcmpcaPrivateCertificateConfig_ARN(csr1, csr2), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttrPair(datasourceName, "arn", resourceName, "arn"), resource.TestCheckResourceAttrPair(datasourceName, "certificate", resourceName, "certificate"), @@ -32,33 +35,8 @@ func TestAccDataSourceAwsAcmpcaPrivateCertificate_Basic(t *testing.T) { }) } -const testAccDataSourceAwsAcmpcaPrivateCertificateConfig_ARN = ` -resource "tls_private_key" "key_1" { - algorithm = "RSA" -} - -resource "tls_cert_request" "csr_1" { - key_algorithm = "RSA" - private_key_pem = tls_private_key.key_1.private_key_pem - - subject { - common_name = "wrong" - } -} - -resource "tls_private_key" "key_2" { - algorithm = "RSA" -} - -resource "tls_cert_request" "csr_2" { - key_algorithm = "RSA" - private_key_pem = tls_private_key.key_2.private_key_pem - - subject { - common_name = "testing" - } -} - +func testAccDataSourceAwsAcmpcaPrivateCertificateConfig_ARN(csr1, csr2 string) string { + return fmt.Sprintf(` resource "aws_acmpca_certificate_authority" "test" { permanent_deletion_time_in_days = 7 type = "ROOT" @@ -75,7 +53,7 @@ resource "aws_acmpca_certificate_authority" "test" { resource "aws_acmpca_private_certificate" "wrong" { certificate_authority_arn = aws_acmpca_certificate_authority.test.arn - certificate_signing_request = tls_cert_request.csr_1.cert_request_pem + certificate_signing_request = "%[1]s" signing_algorithm = "SHA256WITHRSA" validity_length = 1 validity_unit = "YEARS" @@ -83,7 +61,7 @@ resource "aws_acmpca_private_certificate" "wrong" { resource "aws_acmpca_private_certificate" "test" { certificate_authority_arn = aws_acmpca_certificate_authority.test.arn - certificate_signing_request = tls_cert_request.csr_2.cert_request_pem + certificate_signing_request = "%[2]s" signing_algorithm = "SHA256WITHRSA" validity_length = 1 validity_unit = "YEARS" @@ -92,8 +70,8 @@ resource "aws_acmpca_private_certificate" "test" { data "aws_acmpca_private_certificate" "test" { arn = aws_acmpca_private_certificate.test.arn certificate_authority_arn = aws_acmpca_certificate_authority.test.arn +}`, csr1, csr2) } -` const testAccDataSourceAwsAcmpcaPrivateCertificateConfig_NonExistent = ` data "aws_acmpca_private_certificate" "test" { diff --git a/aws/resource_aws_acmpca_private_certificate_test.go b/aws/resource_aws_acmpca_private_certificate_test.go index 7467882aede3..3fde7144aa50 100644 --- a/aws/resource_aws_acmpca_private_certificate_test.go +++ b/aws/resource_aws_acmpca_private_certificate_test.go @@ -13,14 +13,15 @@ import ( func TestAccAwsAcmpcaPrivateCertificate_Basic(t *testing.T) { resourceName := "aws_acmpca_private_certificate.test" + csr, _ := tlsRsaX509CertificateRequestPem(4096, "terraformtest1.com") resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProvidersWithTLS, + Providers: testAccProviders, CheckDestroy: testAccCheckAwsAcmpcaPrivateCertificateDestroy, Steps: []resource.TestStep{ { - Config: testAccAwsAcmpcaPrivateCertificateConfig_Required, + Config: testAccAwsAcmpcaPrivateCertificateConfig_Required(csr), Check: resource.ComposeTestCheckFunc( testAccCheckAwsAcmpcaPrivateCertificateExists(resourceName), resource.TestMatchResourceAttr(resourceName, "arn", regexp.MustCompile(`^arn:[^:]+:acm-pca:[^:]+:[^:]+:certificate-authority/.+/certificate/.+$`)), @@ -70,20 +71,8 @@ func testAccCheckAwsAcmpcaPrivateCertificateExists(resourceName string) resource } } -const testAccAwsAcmpcaPrivateCertificateConfig_Required = ` -resource "tls_private_key" "key" { - algorithm = "RSA" -} - -resource "tls_cert_request" "csr" { - key_algorithm = "RSA" - private_key_pem = tls_private_key.key.private_key_pem - - subject { - common_name = "testing" - } -} - +func testAccAwsAcmpcaPrivateCertificateConfig_Required(csr string) string { + return fmt.Sprintf(` resource "aws_acmpca_certificate_authority" "test" { permanent_deletion_time_in_days = 7 type = "ROOT" @@ -100,9 +89,9 @@ resource "aws_acmpca_certificate_authority" "test" { resource "aws_acmpca_private_certificate" "test" { certificate_authority_arn = aws_acmpca_certificate_authority.test.arn - certificate_signing_request = tls_cert_request.csr.cert_request_pem + certificate_signing_request = "%[1]s" signing_algorithm = "SHA256WITHRSA" validity_length = 1 validity_unit = "YEARS" +}`, csr) } -` diff --git a/aws/tls.go b/aws/tls.go index e91f9ecba779..19098f61dc5d 100644 --- a/aws/tls.go +++ b/aws/tls.go @@ -13,9 +13,10 @@ import ( ) const ( - pemBlockTypeCertificate = `CERTIFICATE` - pemBlockTypeRsaPrivateKey = `RSA PRIVATE KEY` - pemBlockTypePublicKey = `PUBLIC KEY` + pemBlockTypeCertificate = `CERTIFICATE` + pemBlockTypeRsaPrivateKey = `RSA PRIVATE KEY` + pemBlockTypePublicKey = `PUBLIC KEY` + pemBlockTypeCertificateRequest = `CERTIFICATE REQUEST` ) var tlsX509CertificateSerialNumberLimit = new(big.Int).Lsh(big.NewInt(1), 128) @@ -225,6 +226,38 @@ func tlsRsaX509SelfSignedCertificatePem(keyPem, commonName string) string { return string(pem.EncodeToMemory(certificateBlock)) } +func tlsRsaX509CertificateRequestPem(keyBits int, commonName string) (string, string) { + keyBytes, err := rsa.GenerateKey(rand.Reader, keyBits) + if err != nil { + panic(err) + } + + csr := x509.CertificateRequest{ + Subject: pkix.Name{ + CommonName: commonName, + Organization: []string{"ACME Examples, Inc"}, + }, + SignatureAlgorithm: x509.SHA256WithRSA, + } + + csrBytes, err := x509.CreateCertificateRequest(rand.Reader, &csr, keyBytes) + if err != nil { + panic(err) + } + + csrBlock := &pem.Block{ + Bytes: csrBytes, + Type: pemBlockTypeCertificateRequest, + } + + keyBlock := &pem.Block{ + Bytes: x509.MarshalPKCS1PrivateKey(keyBytes), + Type: pemBlockTypeRsaPrivateKey, + } + + return string(pem.EncodeToMemory(csrBlock)), string(pem.EncodeToMemory(keyBlock)) +} + func tlsPemEscapeNewlines(pem string) string { return strings.ReplaceAll(pem, "\n", "\\n") } diff --git a/aws/tls_test.go b/aws/tls_test.go index 315907f4db96..d2c9a99d713d 100644 --- a/aws/tls_test.go +++ b/aws/tls_test.go @@ -50,3 +50,15 @@ func TestTlsRsaX509SelfSignedCertificatePem(t *testing.T) { t.Errorf("certificate does not contain CERTIFICATE: %s", certificate) } } + +func TestTlsRsaX509CertificateRequestPem(t *testing.T) { + csr, key := tlsRsaX509CertificateRequestPem(2048, "example.com") + + if !strings.Contains(csr, pemBlockTypeCertificateRequest) { + t.Errorf("certificate does not contain CERTIFICATE REQUEST: %s", csr) + } + + if !strings.Contains(key, pemBlockTypeRsaPrivateKey) { + t.Errorf("certificate does not contain RSA PRIVATE KEY: %s", key) + } +} diff --git a/website/docs/d/acmpca_private_certificate.html.markdown b/website/docs/d/acmpca_private_certificate.html.markdown index 1f2f9ab760ca..549502d76006 100644 --- a/website/docs/d/acmpca_private_certificate.html.markdown +++ b/website/docs/d/acmpca_private_certificate.html.markdown @@ -14,7 +14,7 @@ Get information on a AWS Certificate Manager Private Certificate Authority (ACM ```hcl data "aws_acmpca_private_certificate" "example" { - arn = "arn:aws:acm-pca:us-east-1:123456789012:certificate-authority/12345678-1234-1234-1234-123456789012/certificate/1234b4a0d73e2056789bdbe77d5b1a23" + arn = "arn:aws:acm-pca:us-east-1:123456789012:certificate-authority/12345678-1234-1234-1234-123456789012/certificate/1234b4a0d73e2056789bdbe77d5b1a23" certificate_authority_arn = "arn:aws:acm-pca:us-east-1:123456789012:certificate-authority/12345678-1234-1234-1234-123456789012" } ``` diff --git a/website/docs/r/acmpca_private_certificate.html.markdown b/website/docs/r/acmpca_private_certificate.html.markdown index 36ed13bfb489..66a2a04d325b 100644 --- a/website/docs/r/acmpca_private_certificate.html.markdown +++ b/website/docs/r/acmpca_private_certificate.html.markdown @@ -42,11 +42,11 @@ resource "tls_cert_request" "csr" { } resource "aws_acmpca_private_certificate" "example" { - certificate_authority_arn = aws_acmpca_certificate_authority.example.arn - certificate_signing_request = tls_cert_request.csr.cert_request_pem - signing_algorithm = "SHA256WITHRSA" - validity_length = 1 - validity_unit = "YEARS" + certificate_authority_arn = aws_acmpca_certificate_authority.example.arn + certificate_signing_request = tls_cert_request.csr.cert_request_pem + signing_algorithm = "SHA256WITHRSA" + validity_length = 1 + validity_unit = "YEARS" } ``` diff --git a/website/docs/r/waf_sql_injection_match_set.html.markdown b/website/docs/r/waf_sql_injection_match_set.html.markdown index 04a5e2603550..08da2041fb7b 100644 --- a/website/docs/r/waf_sql_injection_match_set.html.markdown +++ b/website/docs/r/waf_sql_injection_match_set.html.markdown @@ -70,4 +70,4 @@ AWS WAF SQL Injection Match Set can be imported using their ID, e.g. ``` $ terraform import aws_waf_sql_injection_match_set.example a1b2c3d4-d5f6-7777-8888-9999aaaabbbbcccc -``` \ No newline at end of file +``` From d7f8d6779d3c646f82e3e869835d302927a365e6 Mon Sep 17 00:00:00 2001 From: David Liao Date: Sun, 3 May 2020 16:15:39 -0700 Subject: [PATCH 13/31] lints --- aws/resource_aws_acmpca_private_certificate.go | 12 +----------- website/aws.erb | 4 ++++ 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/aws/resource_aws_acmpca_private_certificate.go b/aws/resource_aws_acmpca_private_certificate.go index aaf30c5e16b0..eddfb05b9785 100644 --- a/aws/resource_aws_acmpca_private_certificate.go +++ b/aws/resource_aws_acmpca_private_certificate.go @@ -113,17 +113,7 @@ func resourceAwsAcmpcaPrivateCertificateCreate(d *schema.ResourceData, meta inte log.Printf("[DEBUG] ACMPCA Issue Certificate: %s", input) var output *acmpca.IssueCertificateOutput - err := resource.Retry(1*time.Minute, func() *resource.RetryError { - var err error - output, err = conn.IssueCertificate(input) - if err != nil { - return resource.NonRetryableError(err) - } - return nil - }) - if isResourceTimeoutError(err) { - output, err = conn.IssueCertificate(input) - } + output, err := conn.IssueCertificate(input) if err != nil { return fmt.Errorf("error issuing ACMPCA Certificate: %s", err) } diff --git a/website/aws.erb b/website/aws.erb index 0a394c12f0b9..c5d2f7dc3b83 100644 --- a/website/aws.erb +++ b/website/aws.erb @@ -103,6 +103,8 @@ @@ -112,6 +114,8 @@ From 285719910ba187aa028859e7d200c8c0774011e4 Mon Sep 17 00:00:00 2001 From: David Liao Date: Sun, 3 May 2020 16:30:47 -0700 Subject: [PATCH 14/31] fixing tests and add comment --- aws/data_source_aws_acmpca_private_certificate_test.go | 2 +- aws/resource_aws_acmpca_private_certificate_test.go | 2 +- aws/tls.go | 4 ++++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/aws/data_source_aws_acmpca_private_certificate_test.go b/aws/data_source_aws_acmpca_private_certificate_test.go index 0f26d3f8fd0b..6face57a248d 100644 --- a/aws/data_source_aws_acmpca_private_certificate_test.go +++ b/aws/data_source_aws_acmpca_private_certificate_test.go @@ -23,7 +23,7 @@ func TestAccDataSourceAwsAcmpcaPrivateCertificate_Basic(t *testing.T) { ExpectError: regexp.MustCompile(`(AccessDeniedException|ResourceNotFoundException)`), }, { - Config: testAccDataSourceAwsAcmpcaPrivateCertificateConfig_ARN(csr1, csr2), + Config: testAccDataSourceAwsAcmpcaPrivateCertificateConfig_ARN(tlsPemEscapeNewlines(csr1), tlsPemEscapeNewlines(csr2)), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttrPair(datasourceName, "arn", resourceName, "arn"), resource.TestCheckResourceAttrPair(datasourceName, "certificate", resourceName, "certificate"), diff --git a/aws/resource_aws_acmpca_private_certificate_test.go b/aws/resource_aws_acmpca_private_certificate_test.go index 3fde7144aa50..ad7def207c73 100644 --- a/aws/resource_aws_acmpca_private_certificate_test.go +++ b/aws/resource_aws_acmpca_private_certificate_test.go @@ -21,7 +21,7 @@ func TestAccAwsAcmpcaPrivateCertificate_Basic(t *testing.T) { CheckDestroy: testAccCheckAwsAcmpcaPrivateCertificateDestroy, Steps: []resource.TestStep{ { - Config: testAccAwsAcmpcaPrivateCertificateConfig_Required(csr), + Config: testAccAwsAcmpcaPrivateCertificateConfig_Required(tlsPemEscapeNewlines(csr)), Check: resource.ComposeTestCheckFunc( testAccCheckAwsAcmpcaPrivateCertificateExists(resourceName), resource.TestMatchResourceAttr(resourceName, "arn", regexp.MustCompile(`^arn:[^:]+:acm-pca:[^:]+:[^:]+:certificate-authority/.+/certificate/.+$`)), diff --git a/aws/tls.go b/aws/tls.go index 19098f61dc5d..f134a571c66e 100644 --- a/aws/tls.go +++ b/aws/tls.go @@ -226,6 +226,10 @@ func tlsRsaX509SelfSignedCertificatePem(keyPem, commonName string) string { return string(pem.EncodeToMemory(certificateBlock)) } +// tlsRsaX509CertificateRequestPem generates a x509 certificate request PEM string +// and a RSA private key PEM string. +// Wrap with tlsPemEscapeNewlines() to allow simple fmt.Sprintf() +// configurations such as: certificate_signing_request_pem = "%[1]s" private_key_pem = "%[2]s" func tlsRsaX509CertificateRequestPem(keyBits int, commonName string) (string, string) { keyBytes, err := rsa.GenerateKey(rand.Reader, keyBits) if err != nil { From 4b368a7b30b61e7808dc8394466ee4a2ca0f482a Mon Sep 17 00:00:00 2001 From: David Liao Date: Mon, 4 May 2020 19:24:08 -0700 Subject: [PATCH 15/31] revert endline --- website/docs/r/waf_sql_injection_match_set.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/waf_sql_injection_match_set.html.markdown b/website/docs/r/waf_sql_injection_match_set.html.markdown index 08da2041fb7b..04a5e2603550 100644 --- a/website/docs/r/waf_sql_injection_match_set.html.markdown +++ b/website/docs/r/waf_sql_injection_match_set.html.markdown @@ -70,4 +70,4 @@ AWS WAF SQL Injection Match Set can be imported using their ID, e.g. ``` $ terraform import aws_waf_sql_injection_match_set.example a1b2c3d4-d5f6-7777-8888-9999aaaabbbbcccc -``` +``` \ No newline at end of file From 58142bd8a95ebb9c5283b7ab5774c3a1e887d118 Mon Sep 17 00:00:00 2001 From: David Liao Date: Mon, 4 May 2020 19:29:32 -0700 Subject: [PATCH 16/31] cr --- aws/resource_aws_acmpca_private_certificate.go | 7 ++++--- aws/resource_aws_acmpca_private_certificate_test.go | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/aws/resource_aws_acmpca_private_certificate.go b/aws/resource_aws_acmpca_private_certificate.go index eddfb05b9785..971a92ff867a 100644 --- a/aws/resource_aws_acmpca_private_certificate.go +++ b/aws/resource_aws_acmpca_private_certificate.go @@ -38,9 +38,10 @@ func resourceAwsAcmpcaPrivateCertificate() *schema.Resource { Computed: true, }, "certificate_authority_arn": { - Type: schema.TypeString, - Required: true, - ForceNew: true, + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateArn, }, "certificate_signing_request": { Type: schema.TypeString, diff --git a/aws/resource_aws_acmpca_private_certificate_test.go b/aws/resource_aws_acmpca_private_certificate_test.go index ad7def207c73..9423604a7f78 100644 --- a/aws/resource_aws_acmpca_private_certificate_test.go +++ b/aws/resource_aws_acmpca_private_certificate_test.go @@ -24,7 +24,7 @@ func TestAccAwsAcmpcaPrivateCertificate_Basic(t *testing.T) { Config: testAccAwsAcmpcaPrivateCertificateConfig_Required(tlsPemEscapeNewlines(csr)), Check: resource.ComposeTestCheckFunc( testAccCheckAwsAcmpcaPrivateCertificateExists(resourceName), - resource.TestMatchResourceAttr(resourceName, "arn", regexp.MustCompile(`^arn:[^:]+:acm-pca:[^:]+:[^:]+:certificate-authority/.+/certificate/.+$`)), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "acm-pca", regexp.MustCompile(`certificate-authority/.+/certificate/.+$`)), resource.TestCheckResourceAttrSet(resourceName, "certificate"), resource.TestCheckResourceAttrSet(resourceName, "certificate_chain"), resource.TestCheckResourceAttrSet(resourceName, "certificate_signing_request"), From 5b36e3701ff43e0e137421908788abe97a8b07a0 Mon Sep 17 00:00:00 2001 From: David Liao Date: Mon, 4 May 2020 19:44:43 -0700 Subject: [PATCH 17/31] lintignore --- aws/tls.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/aws/tls.go b/aws/tls.go index c5be84c40f38..bcf279eb8982 100644 --- a/aws/tls.go +++ b/aws/tls.go @@ -248,6 +248,7 @@ func tlsRsaX509SelfSignedCertificatePem(keyPem, commonName string) string { func tlsRsaX509CertificateRequestPem(keyBits int, commonName string) (string, string) { keyBytes, err := rsa.GenerateKey(rand.Reader, keyBits) if err != nil { + //lintignore:R009 panic(err) } @@ -261,6 +262,7 @@ func tlsRsaX509CertificateRequestPem(keyBits int, commonName string) (string, st csrBytes, err := x509.CreateCertificateRequest(rand.Reader, &csr, keyBytes) if err != nil { + //lintignore:R009 panic(err) } From 1c65502caff019df7b1a0e1fa248e49c490b737e Mon Sep 17 00:00:00 2001 From: David Liao Date: Tue, 5 May 2020 13:34:17 -0700 Subject: [PATCH 18/31] Update resource_aws_acmpca_private_certificate.go --- aws/resource_aws_acmpca_private_certificate.go | 1 - 1 file changed, 1 deletion(-) diff --git a/aws/resource_aws_acmpca_private_certificate.go b/aws/resource_aws_acmpca_private_certificate.go index 971a92ff867a..3dd42368a3b3 100644 --- a/aws/resource_aws_acmpca_private_certificate.go +++ b/aws/resource_aws_acmpca_private_certificate.go @@ -113,7 +113,6 @@ func resourceAwsAcmpcaPrivateCertificateCreate(d *schema.ResourceData, meta inte } log.Printf("[DEBUG] ACMPCA Issue Certificate: %s", input) - var output *acmpca.IssueCertificateOutput output, err := conn.IssueCertificate(input) if err != nil { return fmt.Errorf("error issuing ACMPCA Certificate: %s", err) From d4505bcf8c3b5b5e8e18049ceaf73c8f6746e73b Mon Sep 17 00:00:00 2001 From: David Liao Date: Mon, 1 Jun 2020 23:22:13 -0700 Subject: [PATCH 19/31] cr --- aws/data_source_aws_acmpca_private_certificate.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/aws/data_source_aws_acmpca_private_certificate.go b/aws/data_source_aws_acmpca_private_certificate.go index 7443a2a42d7b..1371ec66037e 100644 --- a/aws/data_source_aws_acmpca_private_certificate.go +++ b/aws/data_source_aws_acmpca_private_certificate.go @@ -15,12 +15,14 @@ func dataSourceAwsAcmpcaPrivateCertificate() *schema.Resource { Schema: map[string]*schema.Schema{ "arn": { - Type: schema.TypeString, - Required: true, + Type: schema.TypeString, + Required: true, + ValidateFunc: validateArn, }, "certificate_authority_arn": { - Type: schema.TypeString, - Required: true, + Type: schema.TypeString, + Required: true, + ValidateFunc: validateArn, }, "certificate": { Type: schema.TypeString, From 2096bf0e2d42c9cdb3f169813f883b2b8676dd4c Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Mon, 22 Feb 2021 17:01:45 -0800 Subject: [PATCH 20/31] Makes ARNs partition-independent --- ...resource_aws_acmpca_private_certificate.go | 47 ++++++++++++++----- ...rce_aws_acmpca_private_certificate_test.go | 31 +++++++++++- 2 files changed, 65 insertions(+), 13 deletions(-) diff --git a/aws/resource_aws_acmpca_private_certificate.go b/aws/resource_aws_acmpca_private_certificate.go index 1194ff68b198..8b7fc15c48d3 100644 --- a/aws/resource_aws_acmpca_private_certificate.go +++ b/aws/resource_aws_acmpca_private_certificate.go @@ -5,9 +5,11 @@ import ( "encoding/pem" "fmt" "log" + "strings" "time" "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/arn" "github.com/aws/aws-sdk-go/service/acmpca" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" @@ -67,18 +69,10 @@ func resourceAwsAcmpcaPrivateCertificate() *schema.Resource { ValidateFunc: validation.StringInSlice(acmpca.ValidityPeriodType_Values(), false), }, "template_arn": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - ValidateFunc: validation.StringInSlice([]string{ - "arn:aws:acm-pca:::template/EndEntityCertificate/V1", - "arn:aws:acm-pca:::template/SubordinateCACertificate_PathLen0/V1", - "arn:aws:acm-pca:::template/SubordinateCACertificate_PathLen1/V1", - "arn:aws:acm-pca:::template/SubordinateCACertificate_PathLen2/V1", - "arn:aws:acm-pca:::template/SubordinateCACertificate_PathLen3/V1", - "arn:aws:acm-pca:::template/RootCACertificate/V1", - }, false), - Default: "arn:aws:acm-pca:::template/EndEntityCertificate/V1", + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validateAcmPcaTemplateArn, }, }, } @@ -179,3 +173,32 @@ func resourceAwsAcmpcaPrivateCertificateRevoke(d *schema.ResourceData, meta inte return nil } + +func validateAcmPcaTemplateArn(v interface{}, k string) (ws []string, errors []error) { + wsARN, errorsARN := validateArn(v, k) + ws = append(ws, wsARN...) + errors = append(errors, errorsARN...) + + if len(errors) == 0 { + value := v.(string) + parsedARN, _ := arn.Parse(value) + + if parsedARN.Service != acmpca.ServiceName { + errors = append(errors, fmt.Errorf("%q (%s) is not a valid ACM PCA template ARN: service must be \""+acmpca.ServiceName+"\", was %q)", k, value, parsedARN.Service)) + } + + if parsedARN.Region != "" { + errors = append(errors, fmt.Errorf("%q (%s) is not a valid ACM PCA template ARN: region must be empty, was %q)", k, value, parsedARN.Region)) + } + + if parsedARN.AccountID != "" { + errors = append(errors, fmt.Errorf("%q (%s) is not a valid ACM PCA template ARN: account ID must be empty, was %q)", k, value, parsedARN.AccountID)) + } + + if !strings.HasPrefix(parsedARN.Resource, "template/") { + errors = append(errors, fmt.Errorf("%q (%s) is not a valid ACM PCA template ARN: expected resource to start with \"template/\", was %q)", k, value, parsedARN.Resource)) + } + } + + return ws, errors +} diff --git a/aws/resource_aws_acmpca_private_certificate_test.go b/aws/resource_aws_acmpca_private_certificate_test.go index 3e1b3d763816..aa090eb020b5 100644 --- a/aws/resource_aws_acmpca_private_certificate_test.go +++ b/aws/resource_aws_acmpca_private_certificate_test.go @@ -31,7 +31,7 @@ func TestAccAwsAcmpcaPrivateCertificate_Basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "validity_length", "1"), resource.TestCheckResourceAttr(resourceName, "validity_unit", "YEARS"), resource.TestCheckResourceAttr(resourceName, "signing_algorithm", "SHA256WITHRSA"), - resource.TestCheckResourceAttr(resourceName, "template_arn", "arn:aws:acm-pca:::template/EndEntityCertificate/V1"), + testAccCheckResourceAttrRegionalARN(resourceName, "template_arn", "acm-pca", "template/EndEntityCertificate/V1"), ), }, }, @@ -96,3 +96,32 @@ resource "aws_acmpca_certificate_authority" "test" { } `, csr) } + +func TestValidateAcmPcaTemplateArn(t *testing.T) { + validNames := []string{ + "arn:aws:acm-pca:::template/EndEntityCertificate/V1", // lintignore:AWSAT005 + "arn:aws:acm-pca:::template/SubordinateCACertificate_PathLen0/V1", // lintignore:AWSAT005 + "arn:aws-us-gov:acm-pca:::template/EndEntityCertificate/V1", // lintignore:AWSAT005 + "arn:aws-us-gov:acm-pca:::template/SubordinateCACertificate_PathLen0/V1", // lintignore:AWSAT005 + } + for _, v := range validNames { + _, errors := validateAcmPcaTemplateArn(v, "template_arn") + if len(errors) != 0 { + t.Fatalf("%q should be a valid ACM PCA ARN: %q", v, errors) + } + } + + invalidNames := []string{ + "arn", + "arn:aws:s3:::my_corporate_bucket/exampleobject.png", // lintignore:AWSAT005 + "arn:aws:acm-pca:us-west-2::template/SubordinateCACertificate_PathLen0/V1", // lintignore:AWSAT003,AWSAT005 + "arn:aws:acm-pca::123456789012:template/EndEntityCertificate/V1", // lintignore:AWSAT005 + "arn:aws:acm-pca:::not-a-template/SubordinateCACertificate_PathLen0/V1", // lintignore:AWSAT005 + } + for _, v := range invalidNames { + _, errors := validateAcmPcaTemplateArn(v, "template_arn") + if len(errors) == 0 { + t.Fatalf("%q should be an invalid ARN", v) + } + } +} From 5ed422b7706f076c158e5ed1546784bad5bfd86a Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Tue, 23 Feb 2021 16:21:40 -0800 Subject: [PATCH 21/31] Supports creating root certificates for private CA --- ...resource_aws_acmpca_private_certificate.go | 21 ++-- ...rce_aws_acmpca_private_certificate_test.go | 102 ++++++++++++++++-- 2 files changed, 108 insertions(+), 15 deletions(-) diff --git a/aws/resource_aws_acmpca_private_certificate.go b/aws/resource_aws_acmpca_private_certificate.go index 8b7fc15c48d3..1aaa3b67f8d5 100644 --- a/aws/resource_aws_acmpca_private_certificate.go +++ b/aws/resource_aws_acmpca_private_certificate.go @@ -11,6 +11,7 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/arn" "github.com/aws/aws-sdk-go/service/acmpca" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" @@ -86,12 +87,14 @@ func resourceAwsAcmpcaPrivateCertificateCreate(d *schema.ResourceData, meta inte Csr: []byte(d.Get("certificate_signing_request").(string)), IdempotencyToken: aws.String(resource.UniqueId()), SigningAlgorithm: aws.String(d.Get("signing_algorithm").(string)), - TemplateArn: aws.String(d.Get("template_arn").(string)), Validity: &acmpca.Validity{ Type: aws.String(d.Get("validity_unit").(string)), Value: aws.Int64(int64(d.Get("validity_length").(int))), }, } + if v, ok := d.Get("template_arn").(string); ok && v != "" { + input.TemplateArn = aws.String(v) + } log.Printf("[DEBUG] ACMPCA Issue Certificate: %s", input) output, err := conn.IssueCertificate(input) @@ -159,16 +162,16 @@ func resourceAwsAcmpcaPrivateCertificateRevoke(d *schema.ResourceData, meta inte CertificateSerial: aws.String(fmt.Sprintf("%x", cert.SerialNumber)), RevocationReason: aws.String(acmpca.RevocationReasonUnspecified), } - - log.Printf("[DEBUG] Revoking ACMPCA Certificate: %s", input) _, err = conn.RevokeCertificate(input) + + if tfawserr.ErrCodeEquals(err, acmpca.ErrCodeResourceNotFoundException) || + tfawserr.ErrCodeEquals(err, acmpca.ErrCodeRequestAlreadyProcessedException) || + tfawserr.ErrCodeEquals(err, acmpca.ErrCodeRequestInProgressException) || + tfawserr.ErrMessageContains(err, acmpca.ErrCodeInvalidRequestException, "Self-signed certificate can not be revoked") { + return nil + } if err != nil { - if isAWSErr(err, acmpca.ErrCodeResourceNotFoundException, "") || - isAWSErr(err, acmpca.ErrCodeRequestAlreadyProcessedException, "") || - isAWSErr(err, acmpca.ErrCodeRequestInProgressException, "") { - return nil - } - return fmt.Errorf("error revoking ACMPCA Certificate: %s", err) + return fmt.Errorf("error revoking ACM PCA Certificate: %s", err) } return nil diff --git a/aws/resource_aws_acmpca_private_certificate_test.go b/aws/resource_aws_acmpca_private_certificate_test.go index aa090eb020b5..0b7c5bc1285f 100644 --- a/aws/resource_aws_acmpca_private_certificate_test.go +++ b/aws/resource_aws_acmpca_private_certificate_test.go @@ -7,11 +7,14 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/acmpca" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" ) func TestAccAwsAcmpcaPrivateCertificate_Basic(t *testing.T) { + t.Skip("Non-root certificates not yet supported") + resourceName := "aws_acmpca_private_certificate.test" csr, _ := tlsRsaX509CertificateRequestPem(4096, "terraformtest1.com") @@ -21,8 +24,8 @@ func TestAccAwsAcmpcaPrivateCertificate_Basic(t *testing.T) { CheckDestroy: testAccCheckAwsAcmpcaPrivateCertificateDestroy, Steps: []resource.TestStep{ { - Config: testAccAwsAcmpcaPrivateCertificateConfig_Required(tlsPemEscapeNewlines(csr)), - Check: resource.ComposeTestCheckFunc( + Config: testAccAwsAcmpcaPrivateCertificateConfig_Basic(tlsPemEscapeNewlines(csr)), + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAwsAcmpcaPrivateCertificateExists(resourceName), testAccMatchResourceAttrRegionalARN(resourceName, "arn", "acm-pca", regexp.MustCompile(`certificate-authority/.+/certificate/.+$`)), resource.TestCheckResourceAttrSet(resourceName, "certificate"), @@ -31,7 +34,35 @@ func TestAccAwsAcmpcaPrivateCertificate_Basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "validity_length", "1"), resource.TestCheckResourceAttr(resourceName, "validity_unit", "YEARS"), resource.TestCheckResourceAttr(resourceName, "signing_algorithm", "SHA256WITHRSA"), - testAccCheckResourceAttrRegionalARN(resourceName, "template_arn", "acm-pca", "template/EndEntityCertificate/V1"), + testAccCheckResourceAttrGlobalARNNoAccount(resourceName, "template_arn", "acm-pca", "template/EndEntityCertificate/V1"), + ), + }, + }, + }) +} + +func TestAccAwsAcmpcaPrivateCertificate_RootCertificate(t *testing.T) { + resourceName := "aws_acmpca_private_certificate.test" + certificateAuthorityResourceName := "aws_acmpca_certificate_authority.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsAcmpcaPrivateCertificateDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsAcmpcaPrivateCertificateConfig_RootCertificate(), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAwsAcmpcaPrivateCertificateExists(resourceName), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "acm-pca", regexp.MustCompile(`certificate-authority/.+/certificate/.+$`)), + resource.TestCheckResourceAttrSet(resourceName, "certificate"), + resource.TestCheckResourceAttr(resourceName, "certificate_chain", ""), + resource.TestCheckResourceAttrPair(resourceName, "certificate_authority_arn", certificateAuthorityResourceName, "arn"), + resource.TestCheckResourceAttrSet(resourceName, "certificate_signing_request"), + resource.TestCheckResourceAttr(resourceName, "validity_length", "1"), + resource.TestCheckResourceAttr(resourceName, "validity_unit", "YEARS"), + resource.TestCheckResourceAttr(resourceName, "signing_algorithm", "SHA512WITHRSA"), + testAccCheckResourceAttrGlobalARNNoAccount(resourceName, "template_arn", "acm-pca", "template/RootCACertificate/V1"), ), }, }, @@ -39,8 +70,35 @@ func TestAccAwsAcmpcaPrivateCertificate_Basic(t *testing.T) { } func testAccCheckAwsAcmpcaPrivateCertificateDestroy(s *terraform.State) error { - // unfortunately aws pca does not have an API to determine if a cert has been revoked. - // see: https://docs.aws.amazon.com/acm-pca/latest/userguide/PcaRevokeCert.html + conn := testAccProvider.Meta().(*AWSClient).acmpcaconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_acmpca_private_certificate" { + continue + } + + input := &acmpca.GetCertificateInput{ + CertificateArn: aws.String(rs.Primary.ID), + CertificateAuthorityArn: aws.String(rs.Primary.Attributes["certificate_authority_arn"]), + } + + output, err := conn.GetCertificate(input) + if tfawserr.ErrCodeEquals(err, acmpca.ErrCodeResourceNotFoundException) { + return nil + } + if tfawserr.ErrMessageContains(err, acmpca.ErrCodeInvalidStateException, "not in the correct state to have issued certificates") { + // This is returned when checking root certificates and the certificate has not been associated with the certificate authority + return nil + } + if err != nil { + return err + } + + if output != nil { + return fmt.Errorf("ACM PCA Certificate (%s) still exists", rs.Primary.ID) + } + } + return nil } @@ -71,7 +129,8 @@ func testAccCheckAwsAcmpcaPrivateCertificateExists(resourceName string) resource } } -func testAccAwsAcmpcaPrivateCertificateConfig_Required(csr string) string { +// nolint:unused +func testAccAwsAcmpcaPrivateCertificateConfig_Basic(csr string) string { return fmt.Sprintf(` resource "aws_acmpca_private_certificate" "test" { certificate_authority_arn = aws_acmpca_certificate_authority.test.arn @@ -97,6 +156,37 @@ resource "aws_acmpca_certificate_authority" "test" { `, csr) } +func testAccAwsAcmpcaPrivateCertificateConfig_RootCertificate() string { + return ` +resource "aws_acmpca_private_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_length = 1 + validity_unit = "YEARS" +} + +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 = "terraformtesting.com" + } + } +} + +data "aws_partition" "current" {} +` +} + func TestValidateAcmPcaTemplateArn(t *testing.T) { validNames := []string{ "arn:aws:acm-pca:::template/EndEntityCertificate/V1", // lintignore:AWSAT005 From 927c0bff55cdcaba0c894f5307e5282b51c4ee27 Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Tue, 23 Feb 2021 16:23:08 -0800 Subject: [PATCH 22/31] Updates output to "ACM PCA" from "ACMPCA" to match AWS naming --- ...source_aws_acmpca_certificate_authority.go | 16 ++++----- ...a_source_aws_acmpca_private_certificate.go | 4 +-- ...source_aws_acmpca_certificate_authority.go | 36 +++++++++---------- ...ws_acmpca_certificate_authority_migrate.go | 4 +-- ...e_aws_acmpca_certificate_authority_test.go | 29 ++++++++------- ...resource_aws_acmpca_private_certificate.go | 14 ++++---- ...rce_aws_acmpca_private_certificate_test.go | 2 +- website/docs/r/acm_certificate.html.markdown | 2 +- 8 files changed, 53 insertions(+), 54 deletions(-) diff --git a/aws/data_source_aws_acmpca_certificate_authority.go b/aws/data_source_aws_acmpca_certificate_authority.go index 1047cd971ef3..aaf324a18dc5 100644 --- a/aws/data_source_aws_acmpca_certificate_authority.go +++ b/aws/data_source_aws_acmpca_certificate_authority.go @@ -103,15 +103,15 @@ func dataSourceAwsAcmpcaCertificateAuthorityRead(d *schema.ResourceData, meta in CertificateAuthorityArn: aws.String(certificateAuthorityArn), } - log.Printf("[DEBUG] Reading ACMPCA Certificate Authority: %s", describeCertificateAuthorityInput) + log.Printf("[DEBUG] Reading ACM PCA Certificate Authority: %s", describeCertificateAuthorityInput) describeCertificateAuthorityOutput, err := conn.DescribeCertificateAuthority(describeCertificateAuthorityInput) if err != nil { - return fmt.Errorf("error reading ACMPCA Certificate Authority: %w", err) + return fmt.Errorf("error reading ACM PCA Certificate Authority: %w", err) } if describeCertificateAuthorityOutput.CertificateAuthority == nil { - return fmt.Errorf("error reading ACMPCA Certificate Authority: not found") + return fmt.Errorf("error reading ACM PCA Certificate Authority: not found") } certificateAuthority := describeCertificateAuthorityOutput.CertificateAuthority @@ -131,14 +131,14 @@ func dataSourceAwsAcmpcaCertificateAuthorityRead(d *schema.ResourceData, meta in CertificateAuthorityArn: aws.String(certificateAuthorityArn), } - log.Printf("[DEBUG] Reading ACMPCA Certificate Authority Certificate: %s", getCertificateAuthorityCertificateInput) + log.Printf("[DEBUG] Reading ACM PCA Certificate Authority Certificate: %s", getCertificateAuthorityCertificateInput) getCertificateAuthorityCertificateOutput, err := conn.GetCertificateAuthorityCertificate(getCertificateAuthorityCertificateInput) if err != nil { // Returned when in PENDING_CERTIFICATE status // InvalidStateException: The certificate authority XXXXX is not in the correct state to have a certificate signing request. if !tfawserr.ErrCodeEquals(err, acmpca.ErrCodeInvalidStateException) { - return fmt.Errorf("error reading ACMPCA Certificate Authority Certificate: %w", err) + return fmt.Errorf("error reading ACM PCA Certificate Authority Certificate: %w", err) } } @@ -153,11 +153,11 @@ func dataSourceAwsAcmpcaCertificateAuthorityRead(d *schema.ResourceData, meta in CertificateAuthorityArn: aws.String(certificateAuthorityArn), } - log.Printf("[DEBUG] Reading ACMPCA Certificate Authority Certificate Signing Request: %s", getCertificateAuthorityCsrInput) + log.Printf("[DEBUG] Reading ACM PCA Certificate Authority Certificate Signing Request: %s", getCertificateAuthorityCsrInput) getCertificateAuthorityCsrOutput, err := conn.GetCertificateAuthorityCsr(getCertificateAuthorityCsrInput) if err != nil { - return fmt.Errorf("error reading ACMPCA Certificate Authority Certificate Signing Request: %w", err) + return fmt.Errorf("error reading ACM PCA Certificate Authority Certificate Signing Request: %w", err) } d.Set("certificate_signing_request", "") @@ -168,7 +168,7 @@ func dataSourceAwsAcmpcaCertificateAuthorityRead(d *schema.ResourceData, meta in tags, err := keyvaluetags.AcmpcaListTags(conn, certificateAuthorityArn) if err != nil { - return fmt.Errorf("error listing tags for ACMPCA Certificate Authority (%s): %w", certificateAuthorityArn, err) + return fmt.Errorf("error listing tags for ACM PCA Certificate Authority (%s): %w", certificateAuthorityArn, err) } if err := d.Set("tags", tags.IgnoreAws().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { diff --git a/aws/data_source_aws_acmpca_private_certificate.go b/aws/data_source_aws_acmpca_private_certificate.go index 9592cca566a5..40d6b328a26e 100644 --- a/aws/data_source_aws_acmpca_private_certificate.go +++ b/aws/data_source_aws_acmpca_private_certificate.go @@ -45,11 +45,11 @@ func dataSourceAwsAcmpcaPrivateCertificateRead(d *schema.ResourceData, meta inte CertificateAuthorityArn: aws.String(d.Get("certificate_authority_arn").(string)), } - log.Printf("[DEBUG] Reading ACMPCA Certificate: %s", getCertificateInput) + log.Printf("[DEBUG] Reading ACM PCA Certificate: %s", getCertificateInput) certificateOutput, err := conn.GetCertificate(getCertificateInput) if err != nil { - return fmt.Errorf("error reading ACMPCA Certificate: %s", err) + return fmt.Errorf("error reading ACM PCA Certificate: %s", err) } d.SetId(certificateArn) diff --git a/aws/resource_aws_acmpca_certificate_authority.go b/aws/resource_aws_acmpca_certificate_authority.go index a2ea02cfae9b..36152d3ffdeb 100644 --- a/aws/resource_aws_acmpca_certificate_authority.go +++ b/aws/resource_aws_acmpca_certificate_authority.go @@ -285,7 +285,7 @@ func resourceAwsAcmpcaCertificateAuthorityCreate(d *schema.ResourceData, meta in input.Tags = tags } - log.Printf("[DEBUG] Creating ACMPCA Certificate Authority: %s", input) + log.Printf("[DEBUG] Creating ACM PCA Certificate Authority: %s", input) var output *acmpca.CreateCertificateAuthorityOutput err := resource.Retry(1*time.Minute, func() *resource.RetryError { var err error @@ -303,7 +303,7 @@ func resourceAwsAcmpcaCertificateAuthorityCreate(d *schema.ResourceData, meta in output, err = conn.CreateCertificateAuthority(input) } if err != nil { - return fmt.Errorf("error creating ACMPCA Certificate Authority: %s", err) + return fmt.Errorf("error creating ACM PCA Certificate Authority: %s", err) } d.SetId(aws.StringValue(output.CertificateAuthorityArn)) @@ -311,7 +311,7 @@ func resourceAwsAcmpcaCertificateAuthorityCreate(d *schema.ResourceData, meta in _, err = waiter.CertificateAuthorityCreated(conn, d.Id(), d.Timeout(schema.TimeoutCreate)) if err != nil { - return fmt.Errorf("error waiting for ACMPCA Certificate Authority %q to be active or pending certificate: %s", d.Id(), err) + return fmt.Errorf("error waiting for ACM PCA Certificate Authority %q to be active or pending certificate: %s", d.Id(), err) } return resourceAwsAcmpcaCertificateAuthorityRead(d, meta) @@ -324,17 +324,17 @@ func resourceAwsAcmpcaCertificateAuthorityRead(d *schema.ResourceData, meta inte certificateAuthority, err := finder.CertificateAuthorityByARN(conn, d.Id()) if isAWSErr(err, acmpca.ErrCodeResourceNotFoundException, "") { - log.Printf("[WARN] ACMPCA Certificate Authority %q not found - removing from state", d.Id()) + log.Printf("[WARN] ACM PCA Certificate Authority (%s) not found, removing from state", d.Id()) d.SetId("") return nil } if err != nil { - return fmt.Errorf("error reading ACMPCA Certificate Authority: %s", err) + return fmt.Errorf("error reading ACM PCA Certificate Authority: %s", err) } if certificateAuthority == nil || aws.StringValue(certificateAuthority.Status) == acmpca.CertificateAuthorityStatusDeleted { - log.Printf("[WARN] ACMPCA Certificate Authority %q not found - removing from state", d.Id()) + log.Printf("[WARN] ACM PCA Certificate Authority (%s) not found, removing from state", d.Id()) d.SetId("") return nil } @@ -361,19 +361,19 @@ func resourceAwsAcmpcaCertificateAuthorityRead(d *schema.ResourceData, meta inte CertificateAuthorityArn: aws.String(d.Id()), } - log.Printf("[DEBUG] Reading ACMPCA Certificate Authority Certificate: %s", getCertificateAuthorityCertificateInput) + log.Printf("[DEBUG] Reading ACM PCA Certificate Authority Certificate: %s", getCertificateAuthorityCertificateInput) getCertificateAuthorityCertificateOutput, err := conn.GetCertificateAuthorityCertificate(getCertificateAuthorityCertificateInput) if err != nil { if isAWSErr(err, acmpca.ErrCodeResourceNotFoundException, "") { - log.Printf("[WARN] ACMPCA Certificate Authority %q not found - removing from state", d.Id()) + log.Printf("[WARN] ACM PCA Certificate Authority (%s) not found, removing from state", d.Id()) d.SetId("") return nil } // Returned when in PENDING_CERTIFICATE status // InvalidStateException: The certificate authority XXXXX is not in the correct state to have a certificate signing request. if !isAWSErr(err, acmpca.ErrCodeInvalidStateException, "") { - return fmt.Errorf("error reading ACMPCA Certificate Authority Certificate: %s", err) + return fmt.Errorf("error reading ACM PCA Certificate Authority Certificate: %s", err) } } @@ -388,17 +388,17 @@ func resourceAwsAcmpcaCertificateAuthorityRead(d *schema.ResourceData, meta inte CertificateAuthorityArn: aws.String(d.Id()), } - log.Printf("[DEBUG] Reading ACMPCA Certificate Authority Certificate Signing Request: %s", getCertificateAuthorityCsrInput) + log.Printf("[DEBUG] Reading ACM PCA Certificate Authority Certificate Signing Request: %s", getCertificateAuthorityCsrInput) getCertificateAuthorityCsrOutput, err := conn.GetCertificateAuthorityCsr(getCertificateAuthorityCsrInput) if err != nil { if isAWSErr(err, acmpca.ErrCodeResourceNotFoundException, "") { - log.Printf("[WARN] ACMPCA Certificate Authority %q not found - removing from state", d.Id()) + log.Printf("[WARN] ACM PCA Certificate Authority (%s) not found, removing from state", d.Id()) d.SetId("") return nil } if !isAWSErr(err, acmpca.ErrCodeInvalidStateException, "") { - return fmt.Errorf("error reading ACMPCA Certificate Authority Certificate Signing Request: %s", err) + return fmt.Errorf("error reading ACM PCA Certificate Authority Certificate Signing Request: %s", err) } } @@ -410,7 +410,7 @@ func resourceAwsAcmpcaCertificateAuthorityRead(d *schema.ResourceData, meta inte tags, err := keyvaluetags.AcmpcaListTags(conn, d.Id()) if err != nil { - return fmt.Errorf("error listing tags for ACMPCA Certificate Authority (%s): %s", d.Id(), err) + return fmt.Errorf("error listing tags for ACM PCA Certificate Authority (%s): %s", d.Id(), err) } if err := d.Set("tags", tags.IgnoreAws().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { @@ -442,10 +442,10 @@ func resourceAwsAcmpcaCertificateAuthorityUpdate(d *schema.ResourceData, meta in } if updateCertificateAuthority { - log.Printf("[DEBUG] Updating ACMPCA Certificate Authority: %s", input) + log.Printf("[DEBUG] Updating ACM PCA Certificate Authority: %s", input) _, err := conn.UpdateCertificateAuthority(input) if err != nil { - return fmt.Errorf("error updating ACMPCA Certificate Authority: %s", err) + return fmt.Errorf("error updating ACM PCA Certificate Authority: %s", err) } } @@ -453,7 +453,7 @@ func resourceAwsAcmpcaCertificateAuthorityUpdate(d *schema.ResourceData, meta in o, n := d.GetChange("tags") if err := keyvaluetags.AcmpcaUpdateTags(conn, d.Id(), o, n); err != nil { - return fmt.Errorf("error updating ACMPCA Certificate Authority (%s) tags: %s", d.Id(), err) + return fmt.Errorf("error updating ACM PCA Certificate Authority (%s) tags: %s", d.Id(), err) } } @@ -471,13 +471,13 @@ func resourceAwsAcmpcaCertificateAuthorityDelete(d *schema.ResourceData, meta in input.PermanentDeletionTimeInDays = aws.Int64(int64(v.(int))) } - log.Printf("[DEBUG] Deleting ACMPCA Certificate Authority: %s", input) + log.Printf("[DEBUG] Deleting ACM PCA Certificate Authority: %s", input) _, err := conn.DeleteCertificateAuthority(input) if err != nil { if isAWSErr(err, acmpca.ErrCodeResourceNotFoundException, "") { return nil } - return fmt.Errorf("error deleting ACMPCA Certificate Authority: %s", err) + return fmt.Errorf("error deleting ACM PCA Certificate Authority: %s", err) } return nil diff --git a/aws/resource_aws_acmpca_certificate_authority_migrate.go b/aws/resource_aws_acmpca_certificate_authority_migrate.go index 1e039f8975bd..156ade901ffb 100644 --- a/aws/resource_aws_acmpca_certificate_authority_migrate.go +++ b/aws/resource_aws_acmpca_certificate_authority_migrate.go @@ -10,7 +10,7 @@ import ( func resourceAwsAcmpcaCertificateAuthorityMigrateState(v int, is *terraform.InstanceState, meta interface{}) (*terraform.InstanceState, error) { switch v { case 0: - log.Println("[INFO] Found ACMPCA Certificate Authority state v0; migrating to v1") + log.Println("[INFO] Found ACM PCA Certificate Authority state v0; migrating to v1") return migrateAcmpcaCertificateAuthorityStateV0toV1(is) default: return is, fmt.Errorf("Unexpected schema version: %d", v) @@ -19,7 +19,7 @@ func resourceAwsAcmpcaCertificateAuthorityMigrateState(v int, is *terraform.Inst func migrateAcmpcaCertificateAuthorityStateV0toV1(is *terraform.InstanceState) (*terraform.InstanceState, error) { if is.Empty() || is.Attributes == nil { - log.Println("[DEBUG] Empty ACMPCA Certificate Authority state; nothing to migrate.") + log.Println("[DEBUG] Empty ACM PCA Certificate Authority state; nothing to migrate.") return is, nil } diff --git a/aws/resource_aws_acmpca_certificate_authority_test.go b/aws/resource_aws_acmpca_certificate_authority_test.go index 915d38c260be..15cf92220352 100644 --- a/aws/resource_aws_acmpca_certificate_authority_test.go +++ b/aws/resource_aws_acmpca_certificate_authority_test.go @@ -31,13 +31,13 @@ func testSweepAcmpcaCertificateAuthorities(region string) error { certificateAuthorities, err := listAcmpcaCertificateAuthorities(conn) if err != nil { if testSweepSkipSweepError(err) { - log.Printf("[WARN] Skipping ACMPCA Certificate Authorities sweep for %s: %s", region, err) + log.Printf("[WARN] Skipping ACM PCA Certificate Authorities sweep for %s: %s", region, err) return nil } - return fmt.Errorf("Error retrieving ACMPCA Certificate Authorities: %w", err) + return fmt.Errorf("Error retrieving ACM PCA Certificate Authorities: %w", err) } if len(certificateAuthorities) == 0 { - log.Print("[DEBUG] No ACMPCA Certificate Authorities to sweep") + log.Print("[DEBUG] No ACM PCA Certificate Authorities to sweep") return nil } @@ -47,7 +47,7 @@ func testSweepAcmpcaCertificateAuthorities(region string) error { arn := aws.StringValue(certificateAuthority.Arn) if aws.StringValue(certificateAuthority.Status) == acmpca.CertificateAuthorityStatusActive { - log.Printf("[INFO] Disabling ACMPCA Certificate Authority: %s", arn) + log.Printf("[INFO] Disabling ACM PCA Certificate Authority: %s", arn) _, err := conn.UpdateCertificateAuthority(&acmpca.UpdateCertificateAuthorityInput{ CertificateAuthorityArn: aws.String(arn), Status: aws.String(acmpca.CertificateAuthorityStatusDisabled), @@ -56,14 +56,14 @@ func testSweepAcmpcaCertificateAuthorities(region string) error { continue } if err != nil { - sweeperErr := fmt.Errorf("error disabling ACMPCA Certificate Authority (%s): %w", arn, err) + sweeperErr := fmt.Errorf("error disabling ACM PCA Certificate Authority (%s): %w", arn, err) log.Printf("[ERROR] %s", sweeperErr) sweeperErrs = multierror.Append(sweeperErrs, sweeperErr) continue } } - log.Printf("[INFO] Deleting ACMPCA Certificate Authority: %s", arn) + log.Printf("[INFO] Deleting ACM PCA Certificate Authority: %s", arn) _, err := conn.DeleteCertificateAuthority(&acmpca.DeleteCertificateAuthorityInput{ CertificateAuthorityArn: aws.String(arn), PermanentDeletionTimeInDays: aws.Int64(int64(7)), @@ -72,7 +72,7 @@ func testSweepAcmpcaCertificateAuthorities(region string) error { continue } if err != nil { - sweeperErr := fmt.Errorf("error deleting ACMPCA Certificate Authority (%s): %w", arn, err) + sweeperErr := fmt.Errorf("error deleting ACM PCA Certificate Authority (%s): %w", arn, err) log.Printf("[ERROR] %s", sweeperErr) sweeperErrs = multierror.Append(sweeperErrs, sweeperErr) continue @@ -484,12 +484,11 @@ func testAccCheckAwsAcmpcaCertificateAuthorityDestroy(s *terraform.State) error } if output != nil && output.CertificateAuthority != nil && aws.StringValue(output.CertificateAuthority.Arn) == rs.Primary.ID && aws.StringValue(output.CertificateAuthority.Status) != acmpca.CertificateAuthorityStatusDeleted { - return fmt.Errorf("ACMPCA Certificate Authority %q still exists in non-DELETED state: %s", rs.Primary.ID, aws.StringValue(output.CertificateAuthority.Status)) + return fmt.Errorf("ACM PCA Certificate Authority %q still exists in non-DELETED state: %s", rs.Primary.ID, aws.StringValue(output.CertificateAuthority.Status)) } } return nil - } func testAccCheckAwsAcmpcaCertificateAuthorityExists(resourceName string, certificateAuthority *acmpca.CertificateAuthority) resource.TestCheckFunc { @@ -511,7 +510,7 @@ func testAccCheckAwsAcmpcaCertificateAuthorityExists(resourceName string, certif } if output == nil || output.CertificateAuthority == nil { - return fmt.Errorf("ACMPCA Certificate Authority %q does not exist", rs.Primary.ID) + return fmt.Errorf("ACM PCA Certificate Authority %q does not exist", rs.Primary.ID) } *certificateAuthority = *output.CertificateAuthority @@ -530,7 +529,7 @@ func testAccCheckAwsAcmpcaCertificateAuthorityActivateCA(certificateAuthority *a CertificateAuthorityArn: aws.String(arn), }) if err != nil { - return fmt.Errorf("error getting ACMPCA Certificate Authority (%s) CSR: %s", arn, err) + return fmt.Errorf("error getting ACM PCA Certificate Authority (%s) CSR: %s", arn, err) } issueCertResp, err := conn.IssueCertificate(&acmpca.IssueCertificateInput{ @@ -545,7 +544,7 @@ func testAccCheckAwsAcmpcaCertificateAuthorityActivateCA(certificateAuthority *a }, }) if err != nil { - return fmt.Errorf("error issuing ACMPCA Certificate Authority (%s) Root CA certificate from CSR: %s", arn, err) + return fmt.Errorf("error issuing ACM PCA Certificate Authority (%s) Root CA certificate from CSR: %s", arn, err) } // Wait for certificate status to become ISSUED. @@ -554,7 +553,7 @@ func testAccCheckAwsAcmpcaCertificateAuthorityActivateCA(certificateAuthority *a CertificateArn: issueCertResp.CertificateArn, }) if err != nil { - return fmt.Errorf("error waiting for ACMPCA Certificate Authority (%s) Root CA certificate to become ISSUED: %s", arn, err) + return fmt.Errorf("error waiting for ACM PCA Certificate Authority (%s) Root CA certificate to become ISSUED: %s", arn, err) } getCertResp, err := conn.GetCertificate(&acmpca.GetCertificateInput{ @@ -562,7 +561,7 @@ func testAccCheckAwsAcmpcaCertificateAuthorityActivateCA(certificateAuthority *a CertificateArn: issueCertResp.CertificateArn, }) if err != nil { - return fmt.Errorf("error getting ACMPCA Certificate Authority (%s) issued Root CA certificate: %s", arn, err) + return fmt.Errorf("error getting ACM PCA Certificate Authority (%s) issued Root CA certificate: %s", arn, err) } _, err = conn.ImportCertificateAuthorityCertificate(&acmpca.ImportCertificateAuthorityCertificateInput{ @@ -570,7 +569,7 @@ func testAccCheckAwsAcmpcaCertificateAuthorityActivateCA(certificateAuthority *a Certificate: []byte(aws.StringValue(getCertResp.Certificate)), }) if err != nil { - return fmt.Errorf("error importing ACMPCA Certificate Authority (%s) Root CA certificate: %s", arn, err) + return fmt.Errorf("error importing ACM PCA Certificate Authority (%s) Root CA certificate: %s", arn, err) } return err diff --git a/aws/resource_aws_acmpca_private_certificate.go b/aws/resource_aws_acmpca_private_certificate.go index 1aaa3b67f8d5..3e42d5dd730b 100644 --- a/aws/resource_aws_acmpca_private_certificate.go +++ b/aws/resource_aws_acmpca_private_certificate.go @@ -96,10 +96,10 @@ func resourceAwsAcmpcaPrivateCertificateCreate(d *schema.ResourceData, meta inte input.TemplateArn = aws.String(v) } - log.Printf("[DEBUG] ACMPCA Issue Certificate: %s", input) + log.Printf("[DEBUG] ACM PCA Issue Certificate: %s", input) output, err := conn.IssueCertificate(input) if err != nil { - return fmt.Errorf("error issuing ACMPCA Certificate: %s", err) + return fmt.Errorf("error issuing ACM PCA Certificate: %s", err) } d.SetId(aws.StringValue(output.CertificateArn)) @@ -111,7 +111,7 @@ func resourceAwsAcmpcaPrivateCertificateCreate(d *schema.ResourceData, meta inte err = conn.WaitUntilCertificateIssued(getCertificateInput) if err != nil { - return fmt.Errorf("error waiting for ACMPCA to issue Certificate %q, error: %s", d.Id(), err) + return fmt.Errorf("error waiting for ACM PCA to issue Certificate %q, error: %s", d.Id(), err) } return resourceAwsAcmpcaPrivateCertificateRead(d, meta) @@ -125,16 +125,16 @@ func resourceAwsAcmpcaPrivateCertificateRead(d *schema.ResourceData, meta interf CertificateAuthorityArn: aws.String(d.Get("certificate_authority_arn").(string)), } - log.Printf("[DEBUG] Reading ACMPCA Certificate: %s", getCertificateInput) + log.Printf("[DEBUG] Reading ACM PCA Certificate: %s", getCertificateInput) certificateOutput, err := conn.GetCertificate(getCertificateInput) if err != nil { if isAWSErr(err, acmpca.ErrCodeResourceNotFoundException, "") { - log.Printf("[WARN] ACMPCA Certificate %q not found - removing from state", d.Id()) + log.Printf("[WARN] ACM PCA Certificate %q not found - removing from state", d.Id()) d.SetId("") return nil } - return fmt.Errorf("error reading ACMPCA Certificate: %s", err) + return fmt.Errorf("error reading ACM PCA Certificate: %s", err) } d.Set("arn", d.Id()) @@ -154,7 +154,7 @@ func resourceAwsAcmpcaPrivateCertificateRevoke(d *schema.ResourceData, meta inte } cert, err := x509.ParseCertificate(block.Bytes) if err != nil { - return fmt.Errorf("Failed to parse ACMPCA Certificate: %s", err) + return fmt.Errorf("Failed to parse ACM PCA Certificate: %s", err) } input := &acmpca.RevokeCertificateInput{ diff --git a/aws/resource_aws_acmpca_private_certificate_test.go b/aws/resource_aws_acmpca_private_certificate_test.go index 0b7c5bc1285f..15c818af23c6 100644 --- a/aws/resource_aws_acmpca_private_certificate_test.go +++ b/aws/resource_aws_acmpca_private_certificate_test.go @@ -122,7 +122,7 @@ func testAccCheckAwsAcmpcaPrivateCertificateExists(resourceName string) resource } if output == nil || output.Certificate == nil { - return fmt.Errorf("ACMPCA Certificate %q does not exist", rs.Primary.ID) + return fmt.Errorf("ACM PCA Certificate %q does not exist", rs.Primary.ID) } return nil diff --git a/website/docs/r/acm_certificate.html.markdown b/website/docs/r/acm_certificate.html.markdown index 7e81a6b83a10..632596cc7721 100644 --- a/website/docs/r/acm_certificate.html.markdown +++ b/website/docs/r/acm_certificate.html.markdown @@ -114,7 +114,7 @@ The following arguments are supported: * `certificate_chain` - (Optional) The certificate's PEM-formatted chain * Creating a private CA issued certificate * `domain_name` - (Required) A domain name for which the certificate should be issued - * `certificate_authority_arn` - (Required) ARN of an ACMPCA + * `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. * `tags` - (Optional) A map of tags to assign to the resource. From 0bfdbe7dce2b8669af8a8349d2e9362a208edcad Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Wed, 24 Feb 2021 21:14:58 -0800 Subject: [PATCH 23/31] Adds ability to import certificate into certificate authority --- aws/internal/service/acmpca/finder/finder.go | 27 +++ aws/provider.go | 1 + ...source_aws_acmpca_certificate_authority.go | 30 ++- ...cmpca_certificate_authority_certificate.go | 84 ++++++++ ..._certificate_authority_certificate_test.go | 190 ++++++++++++++++++ ...e_aws_acmpca_certificate_authority_test.go | 63 ++++++ ...resource_aws_acmpca_private_certificate.go | 26 ++- ...rce_aws_acmpca_private_certificate_test.go | 8 +- ...ficate_authority_certificate.html.markdown | 109 ++++++++++ .../acmpca_private_certificate.html.markdown | 19 +- 10 files changed, 529 insertions(+), 28 deletions(-) create mode 100644 aws/resource_aws_acmpca_certificate_authority_certificate.go create mode 100644 aws/resource_aws_acmpca_certificate_authority_certificate_test.go create mode 100644 website/docs/r/acmpca_certificate_authority_certificate.html.markdown diff --git a/aws/internal/service/acmpca/finder/finder.go b/aws/internal/service/acmpca/finder/finder.go index f6dfc51bb5ba..2d4934f4b972 100644 --- a/aws/internal/service/acmpca/finder/finder.go +++ b/aws/internal/service/acmpca/finder/finder.go @@ -3,6 +3,8 @@ package finder import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/acmpca" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" ) // CertificateAuthorityByARN returns the certificate authority corresponding to the specified ARN. @@ -23,3 +25,28 @@ func CertificateAuthorityByARN(conn *acmpca.ACMPCA, arn string) (*acmpca.Certifi return output.CertificateAuthority, nil } + +// CertificateAuthorityCertificateByARN returns the certificate for the certificate authority corresponding to the specified ARN. +// Returns a resource.NotFoundError if no certificate authority is found or the certificate authority does not have a certificate assigned. +func CertificateAuthorityCertificateByARN(conn *acmpca.ACMPCA, arn string) (*acmpca.GetCertificateAuthorityCertificateOutput, error) { + input := &acmpca.GetCertificateAuthorityCertificateInput{ + CertificateAuthorityArn: aws.String(arn), + } + + output, err := conn.GetCertificateAuthorityCertificate(input) + if tfawserr.ErrCodeEquals(err, acmpca.ErrCodeResourceNotFoundException) { + return nil, &resource.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if output == nil { + return nil, &resource.NotFoundError{ + Message: "empty result", + LastRequest: input, + } + } + + return output, nil +} diff --git a/aws/provider.go b/aws/provider.go index 3d7af444175d..a4d1908e0bce 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -410,6 +410,7 @@ func Provider() *schema.Provider { "aws_acm_certificate": resourceAwsAcmCertificate(), "aws_acm_certificate_validation": resourceAwsAcmCertificateValidation(), "aws_acmpca_certificate_authority": resourceAwsAcmpcaCertificateAuthority(), + "aws_acmpca_certificate_authority_certificate": resourceAwsAcmpcaCertificateAuthorityCertificate(), "aws_acmpca_private_certificate": resourceAwsAcmpcaPrivateCertificate(), "aws_ami": resourceAwsAmi(), "aws_ami_copy": resourceAwsAmiCopy(), diff --git a/aws/resource_aws_acmpca_certificate_authority.go b/aws/resource_aws_acmpca_certificate_authority.go index 36152d3ffdeb..2455f26e22a1 100644 --- a/aws/resource_aws_acmpca_certificate_authority.go +++ b/aws/resource_aws_acmpca_certificate_authority.go @@ -7,6 +7,7 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/acmpca" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" @@ -463,21 +464,34 @@ func resourceAwsAcmpcaCertificateAuthorityUpdate(d *schema.ResourceData, meta in func resourceAwsAcmpcaCertificateAuthorityDelete(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).acmpcaconn - input := &acmpca.DeleteCertificateAuthorityInput{ + // The Certificate Authority must be in PENDING_CERTIFICATE or DISABLED state before deleting. + updateInput := &acmpca.UpdateCertificateAuthorityInput{ + CertificateAuthorityArn: aws.String(d.Id()), + Status: aws.String(acmpca.CertificateAuthorityStatusDisabled), + } + _, err := conn.UpdateCertificateAuthority(updateInput) + if tfawserr.ErrCodeEquals(err, acmpca.ErrCodeResourceNotFoundException) { + return nil + } + if err != nil && !tfawserr.ErrMessageContains(err, acmpca.ErrCodeInvalidStateException, "The certificate authority must be in the ACTIVE or DISABLED state to be updated") { + return fmt.Errorf("error setting ACM PCA Certificate Authority (%s) to DISABLED status before deleting: %w", d.Id(), err) + } + + deleteInput := &acmpca.DeleteCertificateAuthorityInput{ CertificateAuthorityArn: aws.String(d.Id()), } if v, exists := d.GetOk("permanent_deletion_time_in_days"); exists { - input.PermanentDeletionTimeInDays = aws.Int64(int64(v.(int))) + deleteInput.PermanentDeletionTimeInDays = aws.Int64(int64(v.(int))) } - log.Printf("[DEBUG] Deleting ACM PCA Certificate Authority: %s", input) - _, err := conn.DeleteCertificateAuthority(input) + log.Printf("[DEBUG] Deleting ACM PCA Certificate Authority: %s", deleteInput) + _, err = conn.DeleteCertificateAuthority(deleteInput) + if tfawserr.ErrCodeEquals(err, acmpca.ErrCodeResourceNotFoundException) { + return nil + } if err != nil { - if isAWSErr(err, acmpca.ErrCodeResourceNotFoundException, "") { - return nil - } - return fmt.Errorf("error deleting ACM PCA Certificate Authority: %s", err) + return fmt.Errorf("error deleting ACM PCA Certificate Authority (%s): %w", d.Id(), err) } return nil diff --git a/aws/resource_aws_acmpca_certificate_authority_certificate.go b/aws/resource_aws_acmpca_certificate_authority_certificate.go new file mode 100644 index 000000000000..30a1784e4fbe --- /dev/null +++ b/aws/resource_aws_acmpca_certificate_authority_certificate.go @@ -0,0 +1,84 @@ +package aws + +import ( + "fmt" + "log" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/acmpca" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/acmpca/finder" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" +) + +func resourceAwsAcmpcaCertificateAuthorityCertificate() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsAcmpcaCertificateAuthorityCertificateCreate, + Read: resourceAwsAcmpcaCertificateAuthorityCertificateRead, + Delete: schema.Noop, + + Schema: map[string]*schema.Schema{ + "certificate": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringLenBetween(1, 32768), + }, + "certificate_authority_arn": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateArn, + }, + "certificate_chain": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringLenBetween(0, 2097152), + }, + }, + } +} + +func resourceAwsAcmpcaCertificateAuthorityCertificateCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).acmpcaconn + + certificateAuthorityArn := d.Get("certificate_authority_arn").(string) + + input := &acmpca.ImportCertificateAuthorityCertificateInput{ + Certificate: []byte(d.Get("certificate").(string)), + CertificateAuthorityArn: aws.String(certificateAuthorityArn), + } + if v, ok := d.Get("certificate_chain").(string); ok && v != "" { + input.CertificateChain = []byte(v) + } + + _, err := conn.ImportCertificateAuthorityCertificate(input) + if err != nil { + return fmt.Errorf("error associating ACM PCA Certificate with Certificate Authority (%s): %w", certificateAuthorityArn, err) + } + + d.SetId(certificateAuthorityArn) + + return resourceAwsAcmpcaCertificateAuthorityCertificateRead(d, meta) +} + +func resourceAwsAcmpcaCertificateAuthorityCertificateRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).acmpcaconn + + output, err := finder.CertificateAuthorityCertificateByARN(conn, d.Id()) + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] ACM PCA Certificate Authority Certificate (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + if err != nil { + return fmt.Errorf("error reading ACM PCA Certificate Authority Certificate (%s): %w", d.Id(), err) + } + + d.Set("certificate", aws.StringValue(output.Certificate)) + d.Set("certificate_chain", aws.StringValue(output.CertificateChain)) + + return nil +} diff --git a/aws/resource_aws_acmpca_certificate_authority_certificate_test.go b/aws/resource_aws_acmpca_certificate_authority_certificate_test.go new file mode 100644 index 000000000000..7011805a515f --- /dev/null +++ b/aws/resource_aws_acmpca_certificate_authority_certificate_test.go @@ -0,0 +1,190 @@ +package aws + +import ( + "fmt" + "testing" + + "github.com/aws/aws-sdk-go/service/acmpca" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/acmpca/finder" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" +) + +func TestAccAwsAcmpcaCertificateAuthorityCertificate_RootCA(t *testing.T) { + var v acmpca.GetCertificateAuthorityCertificateOutput + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_acmpca_certificate_authority_certificate.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: nil, // Certificate authority certificates cannot be deleted + Steps: []resource.TestStep{ + { + Config: testAccAwsAcmpcaCertificateAuthorityCertificate_RootCA(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAwsAcmpcaCertificateAuthorityCertificateExists(resourceName, &v), + resource.TestCheckResourceAttrPair(resourceName, "certificate_authority_arn", "aws_acmpca_certificate_authority.test", "arn"), + resource.TestCheckResourceAttrPair(resourceName, "certificate", "aws_acmpca_private_certificate.test", "certificate"), + resource.TestCheckResourceAttrPair(resourceName, "certificate_chain", "aws_acmpca_private_certificate.test", "certificate_chain"), + ), + }, + }, + }) +} + +func TestAccAwsAcmpcaCertificateAuthorityCertificate_SubordinateCA(t *testing.T) { + var v acmpca.GetCertificateAuthorityCertificateOutput + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_acmpca_certificate_authority_certificate.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: nil, // Certificate authority certificates cannot be deleted + Steps: []resource.TestStep{ + { + Config: testAccAwsAcmpcaCertificateAuthorityCertificate_SubordinateCA(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAwsAcmpcaCertificateAuthorityCertificateExists(resourceName, &v), + resource.TestCheckResourceAttrPair(resourceName, "certificate_authority_arn", "aws_acmpca_certificate_authority.test", "arn"), + resource.TestCheckResourceAttrPair(resourceName, "certificate", "aws_acmpca_private_certificate.test", "certificate"), + resource.TestCheckResourceAttrPair(resourceName, "certificate_chain", "aws_acmpca_private_certificate.test", "certificate_chain"), + ), + }, + }, + }) +} + +func testAccCheckAwsAcmpcaCertificateAuthorityCertificateExists(resourceName string, certificate *acmpca.GetCertificateAuthorityCertificateOutput) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("Not found: %s", resourceName) + } + + conn := testAccProvider.Meta().(*AWSClient).acmpcaconn + + output, err := finder.CertificateAuthorityCertificateByARN(conn, rs.Primary.ID) + if err != nil { + return err + } + if tfresource.NotFound(err) { + return fmt.Errorf("ACM PCA Certificate (%s) does not exist", rs.Primary.ID) + } + + *certificate = *output + + return nil + } +} + +func testAccAwsAcmpcaCertificateAuthorityCertificate_RootCA(rName string) string { + return fmt.Sprintf(` +resource "aws_acmpca_certificate_authority_certificate" "test" { + certificate_authority_arn = aws_acmpca_certificate_authority.test.arn + + certificate = aws_acmpca_private_certificate.test.certificate + certificate_chain = aws_acmpca_private_certificate.test.certificate_chain +} + +resource "aws_acmpca_private_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_length = 1 + validity_unit = "YEARS" +} + +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]s.com" + } + } +} + +data "aws_partition" "current" {} +`, rName) +} + +func testAccAwsAcmpcaCertificateAuthorityCertificate_SubordinateCA(rName string) string { + return fmt.Sprintf(` +resource "aws_acmpca_certificate_authority_certificate" "test" { + certificate_authority_arn = aws_acmpca_certificate_authority.test.arn + + certificate = aws_acmpca_private_certificate.test.certificate + certificate_chain = aws_acmpca_private_certificate.test.certificate_chain +} + +resource "aws_acmpca_private_certificate" "test" { + certificate_authority_arn = aws_acmpca_certificate_authority.root.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/SubordinateCACertificate_PathLen0/V1" + + validity_length = 1 + validity_unit = "YEARS" +} + +resource "aws_acmpca_certificate_authority" "test" { + permanent_deletion_time_in_days = 7 + type = "SUBORDINATE" + + certificate_authority_configuration { + key_algorithm = "RSA_2048" + signing_algorithm = "SHA512WITHRSA" + + subject { + common_name = "sub.%[1]s.com" + } + } +} + +resource "aws_acmpca_certificate_authority" "root" { + permanent_deletion_time_in_days = 7 + type = "ROOT" + + certificate_authority_configuration { + key_algorithm = "RSA_4096" + signing_algorithm = "SHA512WITHRSA" + + subject { + common_name = "%[1]s.com" + } + } +} + +resource "aws_acmpca_certificate_authority_certificate" "root" { + certificate_authority_arn = aws_acmpca_certificate_authority.root.arn + + certificate = aws_acmpca_private_certificate.root.certificate + certificate_chain = aws_acmpca_private_certificate.root.certificate_chain +} + +resource "aws_acmpca_private_certificate" "root" { + certificate_authority_arn = aws_acmpca_certificate_authority.root.arn + certificate_signing_request = aws_acmpca_certificate_authority.root.certificate_signing_request + signing_algorithm = "SHA512WITHRSA" + + template_arn = "arn:${data.aws_partition.current.partition}:acm-pca:::template/RootCACertificate/V1" + + validity_length = 2 + validity_unit = "YEARS" +} + +data "aws_partition" "current" {} +`, rName) +} diff --git a/aws/resource_aws_acmpca_certificate_authority_test.go b/aws/resource_aws_acmpca_certificate_authority_test.go index 15cf92220352..7b64e5459667 100644 --- a/aws/resource_aws_acmpca_certificate_authority_test.go +++ b/aws/resource_aws_acmpca_certificate_authority_test.go @@ -196,6 +196,31 @@ func TestAccAwsAcmpcaCertificateAuthority_Enabled(t *testing.T) { }) } +func TestAccAwsAcmpcaCertificateAuthority_DeleteFromActiveState(t *testing.T) { + var certificateAuthority acmpca.CertificateAuthority + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_acmpca_certificate_authority.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsAcmpcaCertificateAuthorityDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsAcmpcaCertificateAuthorityConfig_WithRootCertificate(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAwsAcmpcaCertificateAuthorityExists(resourceName, &certificateAuthority), + resource.TestCheckResourceAttr(resourceName, "type", acmpca.CertificateAuthorityTypeRoot), + resource.TestCheckResourceAttr(resourceName, "enabled", "true"), + // Since the status of the CA is changed by importing the certificate in + // aws_acmpca_certificate_authority_certificate, the value of `status` is no longer accurate + // resource.TestCheckResourceAttr(resourceName, "status", acmpca.CertificateAuthorityStatusActive), + ), + }, + }, + }) +} + func TestAccAwsAcmpcaCertificateAuthority_RevocationConfiguration_CrlConfiguration_CustomCname(t *testing.T) { var certificateAuthority acmpca.CertificateAuthority rName := acctest.RandomWithPrefix("tf-acc-test") @@ -627,6 +652,44 @@ resource "aws_acmpca_certificate_authority" "test" { `, enabled, certificateAuthorityType, rName) } +func testAccAwsAcmpcaCertificateAuthorityConfig_WithRootCertificate(rName string) string { + return fmt.Sprintf(` +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]s.com" + } + } +} + +resource "aws_acmpca_certificate_authority_certificate" "test" { + certificate_authority_arn = aws_acmpca_certificate_authority.test.arn + + certificate = aws_acmpca_private_certificate.test.certificate + certificate_chain = aws_acmpca_private_certificate.test.certificate_chain +} + +resource "aws_acmpca_private_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_length = 1 + validity_unit = "YEARS" +} + +data "aws_partition" "current" {} +`, rName) +} + const testAccAwsAcmpcaCertificateAuthorityConfig_Required = ` resource "aws_acmpca_certificate_authority" "test" { certificate_authority_configuration { diff --git a/aws/resource_aws_acmpca_private_certificate.go b/aws/resource_aws_acmpca_private_certificate.go index 3e42d5dd730b..56182c510bd0 100644 --- a/aws/resource_aws_acmpca_private_certificate.go +++ b/aws/resource_aws_acmpca_private_certificate.go @@ -25,7 +25,6 @@ func resourceAwsAcmpcaPrivateCertificate() *schema.Resource { Timeouts: &schema.ResourceTimeout{ Create: schema.DefaultTimeout(5 * time.Minute), }, - SchemaVersion: 1, Schema: map[string]*schema.Schema{ "arn": { @@ -82,8 +81,9 @@ func resourceAwsAcmpcaPrivateCertificate() *schema.Resource { func resourceAwsAcmpcaPrivateCertificateCreate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).acmpcaconn + certificateAuthorityArn := d.Get("certificate_authority_arn").(string) input := &acmpca.IssueCertificateInput{ - CertificateAuthorityArn: aws.String(d.Get("certificate_authority_arn").(string)), + CertificateAuthorityArn: aws.String(certificateAuthorityArn), Csr: []byte(d.Get("certificate_signing_request").(string)), IdempotencyToken: aws.String(resource.UniqueId()), SigningAlgorithm: aws.String(d.Get("signing_algorithm").(string)), @@ -96,10 +96,24 @@ func resourceAwsAcmpcaPrivateCertificateCreate(d *schema.ResourceData, meta inte input.TemplateArn = aws.String(v) } - log.Printf("[DEBUG] ACM PCA Issue Certificate: %s", input) - output, err := conn.IssueCertificate(input) + var output *acmpca.IssueCertificateOutput + err := resource.Retry(5*time.Minute, func() *resource.RetryError { + var err error + output, err = conn.IssueCertificate(input) + if tfawserr.ErrMessageContains(err, acmpca.ErrCodeInvalidStateException, "The certificate authority is not in a valid state for issuing certificates") { + return resource.RetryableError(err) + } + if err != nil { + return resource.NonRetryableError(err) + } + return nil + }) + if isResourceTimeoutError(err) { + output, err = conn.IssueCertificate(input) + } + if err != nil { - return fmt.Errorf("error issuing ACM PCA Certificate: %s", err) + return fmt.Errorf("error issuing ACM PCA Certificate with Certificate Authority (%s): %w", certificateAuthorityArn, err) } d.SetId(aws.StringValue(output.CertificateArn)) @@ -111,7 +125,7 @@ func resourceAwsAcmpcaPrivateCertificateCreate(d *schema.ResourceData, meta inte err = conn.WaitUntilCertificateIssued(getCertificateInput) if err != nil { - return fmt.Errorf("error waiting for ACM PCA to issue Certificate %q, error: %s", d.Id(), err) + return fmt.Errorf("error waiting for ACM PCA Certificate Authority (%s) to issue Certificate (%s), error: %w", certificateAuthorityArn, d.Id(), err) } return resourceAwsAcmpcaPrivateCertificateRead(d, meta) diff --git a/aws/resource_aws_acmpca_private_certificate_test.go b/aws/resource_aws_acmpca_private_certificate_test.go index 15c818af23c6..8d74b60c6879 100644 --- a/aws/resource_aws_acmpca_private_certificate_test.go +++ b/aws/resource_aws_acmpca_private_certificate_test.go @@ -163,10 +163,10 @@ resource "aws_acmpca_private_certificate" "test" { 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_length = 1 - validity_unit = "YEARS" + template_arn = "arn:${data.aws_partition.current.partition}:acm-pca:::template/RootCACertificate/V1" + + validity_length = 1 + validity_unit = "YEARS" } resource "aws_acmpca_certificate_authority" "test" { diff --git a/website/docs/r/acmpca_certificate_authority_certificate.html.markdown b/website/docs/r/acmpca_certificate_authority_certificate.html.markdown new file mode 100644 index 000000000000..07fe83e8a767 --- /dev/null +++ b/website/docs/r/acmpca_certificate_authority_certificate.html.markdown @@ -0,0 +1,109 @@ +--- +subcategory: "ACM PCA" +layout: "aws" +page_title: "AWS: aws_acmpca_certificate_authority_certificate" +description: |- + Associates a certificate with an AWS Certificate Manager Private Certificate Authority +--- + +# Resource: aws_acmpca_certificate_authority_certificate + +Associates a certificate with an AWS Certificate Manager Private Certificate Authority (ACM PCA Certificate Authority). An ACM PCA Certificate Authority is unable to issue certificates until it has a certificate associated with it. A root level ACM PCA Certificate Authority is able to self-sign its own root certificate. + +## Example Usage + +### Self-Signed Root Certificate Authority Certificate + +```terraform +resource "aws_acmpca_certificate_authority_certificate" "example" { + certificate_authority_arn = aws_acmpca_certificate_authority.example.arn + + certificate = aws_acmpca_private_certificate.example.certificate + certificate_chain = aws_acmpca_private_certificate.example.certificate_chain +} + +resource "aws_acmpca_private_certificate" "example" { + certificate_authority_arn = aws_acmpca_certificate_authority.example.arn + certificate_signing_request = aws_acmpca_certificate_authority.example.certificate_signing_request + signing_algorithm = "SHA512WITHRSA" + + template_arn = "arn:${data.aws_partition.current.partition}:acm-pca:::template/RootCACertificate/V1" + + validity_length = 1 + validity_unit = "YEARS" +} + +resource "aws_acmpca_certificate_authority" "example" { + type = "ROOT" + + certificate_authority_configuration { + key_algorithm = "RSA_4096" + signing_algorithm = "SHA512WITHRSA" + + subject { + common_name = "example.com" + } + } +} + +data "aws_partition" "current" {} +``` + +### Certificate for Subordinate Certificate Authority + +Note that the certificate for the subordinate certificate authority must be issued by the root certificate authority using a signing request from the subordinate certificate authority. + +```terraform +resource "aws_acmpca_certificate_authority_certificate" "subordinate" { + certificate_authority_arn = aws_acmpca_certificate_authority.subordinate.arn + + certificate = aws_acmpca_private_certificate.subordinate.certificate + certificate_chain = aws_acmpca_private_certificate.subordinate.certificate_chain +} + +resource "aws_acmpca_private_certificate" "subordinate" { + certificate_authority_arn = aws_acmpca_certificate_authority.root.arn + certificate_signing_request = aws_acmpca_certificate_authority.subordinate.certificate_signing_request + signing_algorithm = "SHA512WITHRSA" + + template_arn = "arn:${data.aws_partition.current.partition}:acm-pca:::template/SubordinateCACertificate_PathLen0/V1" + + validity_length = 1 + validity_unit = "YEARS" +} + +resource "aws_acmpca_certificate_authority" "subordinate" { + type = "SUBORDINATE" + + certificate_authority_configuration { + key_algorithm = "RSA_2048" + signing_algorithm = "SHA512WITHRSA" + + subject { + common_name = "sub.example.com" + } + } +} + +resource "aws_acmpca_certificate_authority" "root" { + # ... +} + +resource "aws_acmpca_certificate_authority_certificate" "root" { + # ... +} + +resource "aws_acmpca_private_certificate" "root" { + # ... +} + +data "aws_partition" "current" {} +``` + +## Argument Reference + +The following arguments are supported: + +* `certificate` - (Required) The PEM-encoded certificate for the Certificate Authority. +* `certificate_authority_arn` - (Required) Amazon Resource Name (ARN) of the Certificate Authority. +* `certificate_chain` - (Optional) The PEM-encoded certificate chain that includes any intermediate certificates and chains up to root CA. Required for subordinate Certificate Authorities. Not allowed for root Certificate Authorities. diff --git a/website/docs/r/acmpca_private_certificate.html.markdown b/website/docs/r/acmpca_private_certificate.html.markdown index 66a2a04d325b..621a4e0ae270 100644 --- a/website/docs/r/acmpca_private_certificate.html.markdown +++ b/website/docs/r/acmpca_private_certificate.html.markdown @@ -14,7 +14,15 @@ Provides a resource to manage AWS Certificate Manager Private Certificate Issuin ### Basic -```hcl +```terraform +resource "aws_acmpca_private_certificate" "example" { + certificate_authority_arn = aws_acmpca_certificate_authority.example.arn + certificate_signing_request = tls_cert_request.csr.cert_request_pem + signing_algorithm = "SHA256WITHRSA" + validity_length = 1 + validity_unit = "YEARS" +} + resource "aws_acmpca_certificate_authority" "example" { private_certificate_configuration { key_algorithm = "RSA_4096" @@ -40,14 +48,6 @@ resource "tls_cert_request" "csr" { common_name = "example" } } - -resource "aws_acmpca_private_certificate" "example" { - certificate_authority_arn = aws_acmpca_certificate_authority.example.arn - certificate_signing_request = tls_cert_request.csr.cert_request_pem - signing_algorithm = "SHA256WITHRSA" - validity_length = 1 - validity_unit = "YEARS" -} ``` ## Argument Reference @@ -66,7 +66,6 @@ The following arguments are supported: In addition to all arguments above, the following attributes are exported: -* `id` - Amazon Resource Name (ARN) of the certificate. * `arn` - Amazon Resource Name (ARN) of the certificate. * `certificate` - Certificate PEM. * `certificate_chain` - Certificate chain that includes any intermediate certificates and chains up to root CA that you used to sign your private certificate. From a97e6af1888e63bfcb80f745dda46472bfe556cf Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Wed, 24 Feb 2021 23:33:09 -0800 Subject: [PATCH 24/31] Renamed resource and data source from acmpca_private_certificate to acmpca_certificate --- ... => data_source_aws_acmpca_certificate.go} | 8 +-- ...ata_source_aws_acmpca_certificate_test.go} | 68 +++++++++---------- aws/provider.go | 4 +- ....go => resource_aws_acmpca_certificate.go} | 16 ++--- ..._certificate_authority_certificate_test.go | 26 +++---- ...e_aws_acmpca_certificate_authority_test.go | 6 +- ...> resource_aws_acmpca_certificate_test.go} | 42 ++++++------ .../docs/d/acmpca_certificate.html.markdown | 34 ++++++++++ .../acmpca_private_certificate.html.markdown | 35 ---------- ...kdown => acmpca_certificate.html.markdown} | 18 ++--- ...ficate_authority_certificate.html.markdown | 14 ++-- 11 files changed, 135 insertions(+), 136 deletions(-) rename aws/{data_source_aws_acmpca_private_certificate.go => data_source_aws_acmpca_certificate.go} (82%) rename aws/{data_source_aws_acmpca_private_certificate_test.go => data_source_aws_acmpca_certificate_test.go} (50%) rename aws/{resource_aws_acmpca_private_certificate.go => resource_aws_acmpca_certificate.go} (92%) rename aws/{resource_aws_acmpca_private_certificate_test.go => resource_aws_acmpca_certificate_test.go} (84%) create mode 100644 website/docs/d/acmpca_certificate.html.markdown delete mode 100644 website/docs/d/acmpca_private_certificate.html.markdown rename website/docs/r/{acmpca_private_certificate.html.markdown => acmpca_certificate.html.markdown} (71%) diff --git a/aws/data_source_aws_acmpca_private_certificate.go b/aws/data_source_aws_acmpca_certificate.go similarity index 82% rename from aws/data_source_aws_acmpca_private_certificate.go rename to aws/data_source_aws_acmpca_certificate.go index 40d6b328a26e..6ac3d044f1aa 100644 --- a/aws/data_source_aws_acmpca_private_certificate.go +++ b/aws/data_source_aws_acmpca_certificate.go @@ -9,9 +9,9 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) -func dataSourceAwsAcmpcaPrivateCertificate() *schema.Resource { +func dataSourceAwsAcmpcaCertificate() *schema.Resource { return &schema.Resource{ - Read: dataSourceAwsAcmpcaPrivateCertificateRead, + Read: dataSourceAwsAcmpcaCertificateRead, Schema: map[string]*schema.Schema{ "arn": { @@ -36,7 +36,7 @@ func dataSourceAwsAcmpcaPrivateCertificate() *schema.Resource { } } -func dataSourceAwsAcmpcaPrivateCertificateRead(d *schema.ResourceData, meta interface{}) error { +func dataSourceAwsAcmpcaCertificateRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).acmpcaconn certificateArn := d.Get("arn").(string) @@ -49,7 +49,7 @@ func dataSourceAwsAcmpcaPrivateCertificateRead(d *schema.ResourceData, meta inte certificateOutput, err := conn.GetCertificate(getCertificateInput) if err != nil { - return fmt.Errorf("error reading ACM PCA Certificate: %s", err) + return fmt.Errorf("error reading ACM PCA Certificate (%s): %w", certificateArn, err) } d.SetId(certificateArn) diff --git a/aws/data_source_aws_acmpca_private_certificate_test.go b/aws/data_source_aws_acmpca_certificate_test.go similarity index 50% rename from aws/data_source_aws_acmpca_private_certificate_test.go rename to aws/data_source_aws_acmpca_certificate_test.go index d97b635dd2fb..ab222c2de0d4 100644 --- a/aws/data_source_aws_acmpca_private_certificate_test.go +++ b/aws/data_source_aws_acmpca_certificate_test.go @@ -5,38 +5,54 @@ import ( "regexp" "testing" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" ) -func TestAccDataSourceAwsAcmpcaPrivateCertificate_Basic(t *testing.T) { - resourceName := "aws_acmpca_private_certificate.test" - datasourceName := "data.aws_acmpca_private_certificate.test" - csr1, _ := tlsRsaX509CertificateRequestPem(4096, "terraformtest1.com") - csr2, _ := tlsRsaX509CertificateRequestPem(4096, "terraformtest2.com") +func TestAccDataSourceAwsAcmpcaCertificate_Basic(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_acmpca_certificate.test" + dataSourceName := "data.aws_acmpca_certificate.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, Steps: []resource.TestStep{ { - Config: testAccDataSourceAwsAcmpcaPrivateCertificateConfig_NonExistent, + Config: testAccDataSourceAwsAcmpcaCertificateConfig_NonExistent, ExpectError: regexp.MustCompile(`(AccessDeniedException|ResourceNotFoundException)`), }, { - Config: testAccDataSourceAwsAcmpcaPrivateCertificateConfig_ARN(tlsPemEscapeNewlines(csr1), tlsPemEscapeNewlines(csr2)), + Config: testAccDataSourceAwsAcmpcaCertificateConfig_ARN(rName), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttrPair(datasourceName, "arn", resourceName, "arn"), - resource.TestCheckResourceAttrPair(datasourceName, "certificate", resourceName, "certificate"), - resource.TestCheckResourceAttrPair(datasourceName, "certificate_chain", resourceName, "certificate_chain"), - resource.TestCheckResourceAttrPair(datasourceName, "certificate_authority_arn", resourceName, "certificate_authority_arn"), + resource.TestCheckResourceAttrPair(dataSourceName, "arn", resourceName, "arn"), + resource.TestCheckResourceAttrPair(dataSourceName, "certificate", resourceName, "certificate"), + resource.TestCheckResourceAttrPair(dataSourceName, "certificate_chain", resourceName, "certificate_chain"), + resource.TestCheckResourceAttrPair(dataSourceName, "certificate_authority_arn", resourceName, "certificate_authority_arn"), ), }, }, }) } -func testAccDataSourceAwsAcmpcaPrivateCertificateConfig_ARN(csr1, csr2 string) string { +func testAccDataSourceAwsAcmpcaCertificateConfig_ARN(rName string) string { return fmt.Sprintf(` +data "aws_acmpca_certificate" "test" { + arn = aws_acmpca_certificate.test.arn + certificate_authority_arn = aws_acmpca_certificate_authority.test.arn +} + +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 = "SHA256WITHRSA" + + template_arn = "arn:${data.aws_partition.current.partition}:acm-pca:::template/RootCACertificate/V1" + + validity_length = 1 + validity_unit = "YEARS" +} + resource "aws_acmpca_certificate_authority" "test" { permanent_deletion_time_in_days = 7 type = "ROOT" @@ -46,35 +62,17 @@ resource "aws_acmpca_certificate_authority" "test" { signing_algorithm = "SHA512WITHRSA" subject { - common_name = "terraformtesting.com" + common_name = "%[1]s.com" } } } -resource "aws_acmpca_private_certificate" "wrong" { - certificate_authority_arn = aws_acmpca_certificate_authority.test.arn - certificate_signing_request = "%[1]s" - signing_algorithm = "SHA256WITHRSA" - validity_length = 1 - validity_unit = "YEARS" -} - -resource "aws_acmpca_private_certificate" "test" { - certificate_authority_arn = aws_acmpca_certificate_authority.test.arn - certificate_signing_request = "%[2]s" - signing_algorithm = "SHA256WITHRSA" - validity_length = 1 - validity_unit = "YEARS" -} - -data "aws_acmpca_private_certificate" "test" { - arn = aws_acmpca_private_certificate.test.arn - certificate_authority_arn = aws_acmpca_certificate_authority.test.arn -}`, csr1, csr2) +data "aws_partition" "current" {} +`, rName) } -const testAccDataSourceAwsAcmpcaPrivateCertificateConfig_NonExistent = ` -data "aws_acmpca_private_certificate" "test" { +const testAccDataSourceAwsAcmpcaCertificateConfig_NonExistent = ` +data "aws_acmpca_certificate" "test" { arn = "arn:${data.aws_partition.current.partition}:acm-pca:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:certificate-authority/does-not-exist/certificate/does-not-exist" certificate_authority_arn = "arn:${data.aws_partition.current.partition}:acm-pca:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:certificate-authority/does-not-exist" } diff --git a/aws/provider.go b/aws/provider.go index a4d1908e0bce..bbc441e7097a 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -168,7 +168,7 @@ func Provider() *schema.Provider { DataSourcesMap: map[string]*schema.Resource{ "aws_acm_certificate": dataSourceAwsAcmCertificate(), "aws_acmpca_certificate_authority": dataSourceAwsAcmpcaCertificateAuthority(), - "aws_acmpca_private_certificate": dataSourceAwsAcmpcaPrivateCertificate(), + "aws_acmpca_certificate": dataSourceAwsAcmpcaCertificate(), "aws_ami": dataSourceAwsAmi(), "aws_ami_ids": dataSourceAwsAmiIds(), "aws_api_gateway_api_key": dataSourceAwsApiGatewayApiKey(), @@ -411,7 +411,7 @@ func Provider() *schema.Provider { "aws_acm_certificate_validation": resourceAwsAcmCertificateValidation(), "aws_acmpca_certificate_authority": resourceAwsAcmpcaCertificateAuthority(), "aws_acmpca_certificate_authority_certificate": resourceAwsAcmpcaCertificateAuthorityCertificate(), - "aws_acmpca_private_certificate": resourceAwsAcmpcaPrivateCertificate(), + "aws_acmpca_certificate": resourceAwsAcmpcaCertificate(), "aws_ami": resourceAwsAmi(), "aws_ami_copy": resourceAwsAmiCopy(), "aws_ami_from_instance": resourceAwsAmiFromInstance(), diff --git a/aws/resource_aws_acmpca_private_certificate.go b/aws/resource_aws_acmpca_certificate.go similarity index 92% rename from aws/resource_aws_acmpca_private_certificate.go rename to aws/resource_aws_acmpca_certificate.go index 56182c510bd0..0969a0621b6a 100644 --- a/aws/resource_aws_acmpca_private_certificate.go +++ b/aws/resource_aws_acmpca_certificate.go @@ -17,11 +17,11 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) -func resourceAwsAcmpcaPrivateCertificate() *schema.Resource { +func resourceAwsAcmpcaCertificate() *schema.Resource { return &schema.Resource{ - Create: resourceAwsAcmpcaPrivateCertificateCreate, - Read: resourceAwsAcmpcaPrivateCertificateRead, - Delete: resourceAwsAcmpcaPrivateCertificateRevoke, + Create: resourceAwsAcmpcaCertificateCreate, + Read: resourceAwsAcmpcaCertificateRead, + Delete: resourceAwsAcmpcaCertificateRevoke, Timeouts: &schema.ResourceTimeout{ Create: schema.DefaultTimeout(5 * time.Minute), }, @@ -78,7 +78,7 @@ func resourceAwsAcmpcaPrivateCertificate() *schema.Resource { } } -func resourceAwsAcmpcaPrivateCertificateCreate(d *schema.ResourceData, meta interface{}) error { +func resourceAwsAcmpcaCertificateCreate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).acmpcaconn certificateAuthorityArn := d.Get("certificate_authority_arn").(string) @@ -128,10 +128,10 @@ func resourceAwsAcmpcaPrivateCertificateCreate(d *schema.ResourceData, meta inte return fmt.Errorf("error waiting for ACM PCA Certificate Authority (%s) to issue Certificate (%s), error: %w", certificateAuthorityArn, d.Id(), err) } - return resourceAwsAcmpcaPrivateCertificateRead(d, meta) + return resourceAwsAcmpcaCertificateRead(d, meta) } -func resourceAwsAcmpcaPrivateCertificateRead(d *schema.ResourceData, meta interface{}) error { +func resourceAwsAcmpcaCertificateRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).acmpcaconn getCertificateInput := &acmpca.GetCertificateInput{ @@ -158,7 +158,7 @@ func resourceAwsAcmpcaPrivateCertificateRead(d *schema.ResourceData, meta interf return nil } -func resourceAwsAcmpcaPrivateCertificateRevoke(d *schema.ResourceData, meta interface{}) error { +func resourceAwsAcmpcaCertificateRevoke(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).acmpcaconn block, _ := pem.Decode([]byte(d.Get("certificate").(string))) diff --git a/aws/resource_aws_acmpca_certificate_authority_certificate_test.go b/aws/resource_aws_acmpca_certificate_authority_certificate_test.go index 7011805a515f..5dd42cc6f581 100644 --- a/aws/resource_aws_acmpca_certificate_authority_certificate_test.go +++ b/aws/resource_aws_acmpca_certificate_authority_certificate_test.go @@ -27,8 +27,8 @@ func TestAccAwsAcmpcaCertificateAuthorityCertificate_RootCA(t *testing.T) { Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAwsAcmpcaCertificateAuthorityCertificateExists(resourceName, &v), resource.TestCheckResourceAttrPair(resourceName, "certificate_authority_arn", "aws_acmpca_certificate_authority.test", "arn"), - resource.TestCheckResourceAttrPair(resourceName, "certificate", "aws_acmpca_private_certificate.test", "certificate"), - resource.TestCheckResourceAttrPair(resourceName, "certificate_chain", "aws_acmpca_private_certificate.test", "certificate_chain"), + resource.TestCheckResourceAttrPair(resourceName, "certificate", "aws_acmpca_certificate.test", "certificate"), + resource.TestCheckResourceAttrPair(resourceName, "certificate_chain", "aws_acmpca_certificate.test", "certificate_chain"), ), }, }, @@ -50,8 +50,8 @@ func TestAccAwsAcmpcaCertificateAuthorityCertificate_SubordinateCA(t *testing.T) Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAwsAcmpcaCertificateAuthorityCertificateExists(resourceName, &v), resource.TestCheckResourceAttrPair(resourceName, "certificate_authority_arn", "aws_acmpca_certificate_authority.test", "arn"), - resource.TestCheckResourceAttrPair(resourceName, "certificate", "aws_acmpca_private_certificate.test", "certificate"), - resource.TestCheckResourceAttrPair(resourceName, "certificate_chain", "aws_acmpca_private_certificate.test", "certificate_chain"), + resource.TestCheckResourceAttrPair(resourceName, "certificate", "aws_acmpca_certificate.test", "certificate"), + resource.TestCheckResourceAttrPair(resourceName, "certificate_chain", "aws_acmpca_certificate.test", "certificate_chain"), ), }, }, @@ -86,11 +86,11 @@ func testAccAwsAcmpcaCertificateAuthorityCertificate_RootCA(rName string) string resource "aws_acmpca_certificate_authority_certificate" "test" { certificate_authority_arn = aws_acmpca_certificate_authority.test.arn - certificate = aws_acmpca_private_certificate.test.certificate - certificate_chain = aws_acmpca_private_certificate.test.certificate_chain + certificate = aws_acmpca_certificate.test.certificate + certificate_chain = aws_acmpca_certificate.test.certificate_chain } -resource "aws_acmpca_private_certificate" "test" { +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" @@ -124,11 +124,11 @@ func testAccAwsAcmpcaCertificateAuthorityCertificate_SubordinateCA(rName string) resource "aws_acmpca_certificate_authority_certificate" "test" { certificate_authority_arn = aws_acmpca_certificate_authority.test.arn - certificate = aws_acmpca_private_certificate.test.certificate - certificate_chain = aws_acmpca_private_certificate.test.certificate_chain + certificate = aws_acmpca_certificate.test.certificate + certificate_chain = aws_acmpca_certificate.test.certificate_chain } -resource "aws_acmpca_private_certificate" "test" { +resource "aws_acmpca_certificate" "test" { certificate_authority_arn = aws_acmpca_certificate_authority.root.arn certificate_signing_request = aws_acmpca_certificate_authority.test.certificate_signing_request signing_algorithm = "SHA512WITHRSA" @@ -170,11 +170,11 @@ resource "aws_acmpca_certificate_authority" "root" { resource "aws_acmpca_certificate_authority_certificate" "root" { certificate_authority_arn = aws_acmpca_certificate_authority.root.arn - certificate = aws_acmpca_private_certificate.root.certificate - certificate_chain = aws_acmpca_private_certificate.root.certificate_chain + certificate = aws_acmpca_certificate.root.certificate + certificate_chain = aws_acmpca_certificate.root.certificate_chain } -resource "aws_acmpca_private_certificate" "root" { +resource "aws_acmpca_certificate" "root" { certificate_authority_arn = aws_acmpca_certificate_authority.root.arn certificate_signing_request = aws_acmpca_certificate_authority.root.certificate_signing_request signing_algorithm = "SHA512WITHRSA" diff --git a/aws/resource_aws_acmpca_certificate_authority_test.go b/aws/resource_aws_acmpca_certificate_authority_test.go index 7b64e5459667..30aac6fc17b5 100644 --- a/aws/resource_aws_acmpca_certificate_authority_test.go +++ b/aws/resource_aws_acmpca_certificate_authority_test.go @@ -671,11 +671,11 @@ resource "aws_acmpca_certificate_authority" "test" { resource "aws_acmpca_certificate_authority_certificate" "test" { certificate_authority_arn = aws_acmpca_certificate_authority.test.arn - certificate = aws_acmpca_private_certificate.test.certificate - certificate_chain = aws_acmpca_private_certificate.test.certificate_chain + certificate = aws_acmpca_certificate.test.certificate + certificate_chain = aws_acmpca_certificate.test.certificate_chain } -resource "aws_acmpca_private_certificate" "test" { +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" diff --git a/aws/resource_aws_acmpca_private_certificate_test.go b/aws/resource_aws_acmpca_certificate_test.go similarity index 84% rename from aws/resource_aws_acmpca_private_certificate_test.go rename to aws/resource_aws_acmpca_certificate_test.go index 8d74b60c6879..67775c9262f3 100644 --- a/aws/resource_aws_acmpca_private_certificate_test.go +++ b/aws/resource_aws_acmpca_certificate_test.go @@ -8,25 +8,26 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/acmpca" "github.com/hashicorp/aws-sdk-go-base/tfawserr" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" ) -func TestAccAwsAcmpcaPrivateCertificate_Basic(t *testing.T) { +func TestAccAwsAcmpcaCertificate_Basic(t *testing.T) { t.Skip("Non-root certificates not yet supported") - resourceName := "aws_acmpca_private_certificate.test" + resourceName := "aws_acmpca_certificate.test" csr, _ := tlsRsaX509CertificateRequestPem(4096, "terraformtest1.com") resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, - CheckDestroy: testAccCheckAwsAcmpcaPrivateCertificateDestroy, + CheckDestroy: testAccCheckAwsAcmpcaCertificateDestroy, Steps: []resource.TestStep{ { - Config: testAccAwsAcmpcaPrivateCertificateConfig_Basic(tlsPemEscapeNewlines(csr)), + Config: testAccAwsAcmpcaCertificateConfig_Basic(tlsPemEscapeNewlines(csr)), Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckAwsAcmpcaPrivateCertificateExists(resourceName), + testAccCheckAwsAcmpcaCertificateExists(resourceName), testAccMatchResourceAttrRegionalARN(resourceName, "arn", "acm-pca", regexp.MustCompile(`certificate-authority/.+/certificate/.+$`)), resource.TestCheckResourceAttrSet(resourceName, "certificate"), resource.TestCheckResourceAttrSet(resourceName, "certificate_chain"), @@ -41,19 +42,20 @@ func TestAccAwsAcmpcaPrivateCertificate_Basic(t *testing.T) { }) } -func TestAccAwsAcmpcaPrivateCertificate_RootCertificate(t *testing.T) { - resourceName := "aws_acmpca_private_certificate.test" +func TestAccAwsAcmpcaCertificate_RootCertificate(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_acmpca_certificate.test" certificateAuthorityResourceName := "aws_acmpca_certificate_authority.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, - CheckDestroy: testAccCheckAwsAcmpcaPrivateCertificateDestroy, + CheckDestroy: testAccCheckAwsAcmpcaCertificateDestroy, Steps: []resource.TestStep{ { - Config: testAccAwsAcmpcaPrivateCertificateConfig_RootCertificate(), + Config: testAccAwsAcmpcaCertificateConfig_RootCertificate(rName), Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckAwsAcmpcaPrivateCertificateExists(resourceName), + testAccCheckAwsAcmpcaCertificateExists(resourceName), testAccMatchResourceAttrRegionalARN(resourceName, "arn", "acm-pca", regexp.MustCompile(`certificate-authority/.+/certificate/.+$`)), resource.TestCheckResourceAttrSet(resourceName, "certificate"), resource.TestCheckResourceAttr(resourceName, "certificate_chain", ""), @@ -69,11 +71,11 @@ func TestAccAwsAcmpcaPrivateCertificate_RootCertificate(t *testing.T) { }) } -func testAccCheckAwsAcmpcaPrivateCertificateDestroy(s *terraform.State) error { +func testAccCheckAwsAcmpcaCertificateDestroy(s *terraform.State) error { conn := testAccProvider.Meta().(*AWSClient).acmpcaconn for _, rs := range s.RootModule().Resources { - if rs.Type != "aws_acmpca_private_certificate" { + if rs.Type != "aws_acmpca_certificate" { continue } @@ -102,7 +104,7 @@ func testAccCheckAwsAcmpcaPrivateCertificateDestroy(s *terraform.State) error { return nil } -func testAccCheckAwsAcmpcaPrivateCertificateExists(resourceName string) resource.TestCheckFunc { +func testAccCheckAwsAcmpcaCertificateExists(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[resourceName] if !ok { @@ -130,9 +132,9 @@ func testAccCheckAwsAcmpcaPrivateCertificateExists(resourceName string) resource } // nolint:unused -func testAccAwsAcmpcaPrivateCertificateConfig_Basic(csr string) string { +func testAccAwsAcmpcaCertificateConfig_Basic(csr string) string { return fmt.Sprintf(` -resource "aws_acmpca_private_certificate" "test" { +resource "aws_acmpca_certificate" "test" { certificate_authority_arn = aws_acmpca_certificate_authority.test.arn certificate_signing_request = "%[1]s" signing_algorithm = "SHA256WITHRSA" @@ -156,9 +158,9 @@ resource "aws_acmpca_certificate_authority" "test" { `, csr) } -func testAccAwsAcmpcaPrivateCertificateConfig_RootCertificate() string { - return ` -resource "aws_acmpca_private_certificate" "test" { +func testAccAwsAcmpcaCertificateConfig_RootCertificate(rName string) string { + return fmt.Sprintf(` +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" @@ -178,13 +180,13 @@ resource "aws_acmpca_certificate_authority" "test" { signing_algorithm = "SHA512WITHRSA" subject { - common_name = "terraformtesting.com" + common_name = "%[1]s.com" } } } data "aws_partition" "current" {} -` +`, rName) } func TestValidateAcmPcaTemplateArn(t *testing.T) { diff --git a/website/docs/d/acmpca_certificate.html.markdown b/website/docs/d/acmpca_certificate.html.markdown new file mode 100644 index 000000000000..f356c75636bd --- /dev/null +++ b/website/docs/d/acmpca_certificate.html.markdown @@ -0,0 +1,34 @@ +--- +subcategory: "ACM PCA" +layout: "aws" +page_title: "AWS: aws_acmpca_certificate" +description: |- + Get information on a Certificate issued by a AWS Certificate Manager Private Certificate Authority +--- + +# Data Source: aws_acmpca_certificate + +Get information on a Certificate issued by a AWS Certificate Manager Private Certificate Authority. + +## Example Usage + +```terraform +data "aws_acmpca_certificate" "example" { + arn = "arn:aws:acm-pca:us-east-1:123456789012:certificate-authority/12345678-1234-1234-1234-123456789012/certificate/1234b4a0d73e2056789bdbe77d5b1a23" + certificate_authority_arn = "arn:aws:acm-pca:us-east-1:123456789012:certificate-authority/12345678-1234-1234-1234-123456789012" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `arn` - (Required) Amazon Resource Name (ARN) of the certificate issued by the private certificate authority. +* `certificate_authority_arn` - (Required) Amazon Resource Name (ARN) of the certificate authority. + +## Attribute Reference + +In addition to all arguments above, the following attributes are exported: + +* `certificate` - The PEM-encoded certificate value. +* `certificate_chain` - The PEM-encoded certificate chain that includes any intermediate certificates and chains up to root CA. diff --git a/website/docs/d/acmpca_private_certificate.html.markdown b/website/docs/d/acmpca_private_certificate.html.markdown deleted file mode 100644 index 549502d76006..000000000000 --- a/website/docs/d/acmpca_private_certificate.html.markdown +++ /dev/null @@ -1,35 +0,0 @@ ---- -subcategory: "ACM PCA" -layout: "aws" -page_title: "AWS: aws_acmpca_private_certificate" -description: |- - Get information on a AWS Certificate Manager Private Certificate Issuing ---- - -# Data Source: aws_acmpca_private_certificate - -Get information on a AWS Certificate Manager Private Certificate Authority (ACM PCA Certificate Authority). - -## Example Usage - -```hcl -data "aws_acmpca_private_certificate" "example" { - arn = "arn:aws:acm-pca:us-east-1:123456789012:certificate-authority/12345678-1234-1234-1234-123456789012/certificate/1234b4a0d73e2056789bdbe77d5b1a23" - certificate_authority_arn = "arn:aws:acm-pca:us-east-1:123456789012:certificate-authority/12345678-1234-1234-1234-123456789012" -} -``` - -## Argument Reference - -The following arguments are supported: - -* `arn` - (Required) Amazon Resource Name (ARN) of the certificate issued by the private certificate authority. -* `certificate_authority_arn` - (Required) Amazon Resource Name (ARN) of the certificate authority. - -## Attribute Reference - -In addition to all arguments above, the following attributes are exported: - -* `id` - Amazon Resource Name (ARN) of the certificate authority. -* `certificate` - Base64-encoded certificate authority (CA) certificate. Only available after the certificate authority certificate has been imported. -* `certificate_chain` - Base64-encoded certificate chain that includes any intermediate certificates and chains up to root on-premises certificate that you used to sign your private CA certificate. The chain does not include your private CA certificate. Only available after the certificate authority certificate has been imported. diff --git a/website/docs/r/acmpca_private_certificate.html.markdown b/website/docs/r/acmpca_certificate.html.markdown similarity index 71% rename from website/docs/r/acmpca_private_certificate.html.markdown rename to website/docs/r/acmpca_certificate.html.markdown index 621a4e0ae270..8a9ef19e60a7 100644 --- a/website/docs/r/acmpca_private_certificate.html.markdown +++ b/website/docs/r/acmpca_certificate.html.markdown @@ -1,21 +1,21 @@ --- subcategory: "ACM PCA" layout: "aws" -page_title: "AWS: aws_acmpca_private_certificate" +page_title: "AWS: aws_acmpca_certificate" description: |- - Provides a resource to manage AWS Certificate Manager Private Certificate Issuing + Provides a resource to issue a certificate using AWS Certificate Manager Private Certificate Authority (ACM PCA) --- -# Resource: aws_acmpca_private_certificate +# Resource: aws_acmpca_certificate -Provides a resource to manage AWS Certificate Manager Private Certificate Issuing (ACM PCA Certificate Issuing). It is to be used when signing a certificate that was generated outside AWS and have `aws_acmpca_certificate_authority` as the CA. +Provides a resource to issue a certificate using AWS Certificate Manager Private Certificate Authority (ACM PCA). ## Example Usage ### Basic ```terraform -resource "aws_acmpca_private_certificate" "example" { +resource "aws_acmpca_certificate" "example" { certificate_authority_arn = aws_acmpca_certificate_authority.example.arn certificate_signing_request = tls_cert_request.csr.cert_request_pem signing_algorithm = "SHA256WITHRSA" @@ -67,16 +67,16 @@ The following arguments are supported: In addition to all arguments above, the following attributes are exported: * `arn` - Amazon Resource Name (ARN) of the certificate. -* `certificate` - Certificate PEM. -* `certificate_chain` - Certificate chain that includes any intermediate certificates and chains up to root CA that you used to sign your private certificate. +* `certificate` - The PEM-encoded certificate value. +* `certificate_chain` - The PEM-encoded certificate chain that includes any intermediate certificates and chains up to root CA. ## Timeouts -`aws_acmpca_private_certificate` provides the following [Timeouts](/docs/configuration/resources.html#timeouts) +`aws_acmpca_certificate` provides the following [Timeouts](/docs/configuration/resources.html#timeouts) configuration options: * `create` - (Default `5m`) How long to wait for a certificate authority to issue a certificate. ## Import -`aws_acmpca_private_certificate` can not be imported at this time. +`aws_acmpca_certificate` can not be imported at this time. diff --git a/website/docs/r/acmpca_certificate_authority_certificate.html.markdown b/website/docs/r/acmpca_certificate_authority_certificate.html.markdown index 07fe83e8a767..b5e7fe5c0022 100644 --- a/website/docs/r/acmpca_certificate_authority_certificate.html.markdown +++ b/website/docs/r/acmpca_certificate_authority_certificate.html.markdown @@ -18,11 +18,11 @@ Associates a certificate with an AWS Certificate Manager Private Certificate Aut resource "aws_acmpca_certificate_authority_certificate" "example" { certificate_authority_arn = aws_acmpca_certificate_authority.example.arn - certificate = aws_acmpca_private_certificate.example.certificate - certificate_chain = aws_acmpca_private_certificate.example.certificate_chain + certificate = aws_acmpca_certificate.example.certificate + certificate_chain = aws_acmpca_certificate.example.certificate_chain } -resource "aws_acmpca_private_certificate" "example" { +resource "aws_acmpca_certificate" "example" { certificate_authority_arn = aws_acmpca_certificate_authority.example.arn certificate_signing_request = aws_acmpca_certificate_authority.example.certificate_signing_request signing_algorithm = "SHA512WITHRSA" @@ -57,11 +57,11 @@ Note that the certificate for the subordinate certificate authority must be issu resource "aws_acmpca_certificate_authority_certificate" "subordinate" { certificate_authority_arn = aws_acmpca_certificate_authority.subordinate.arn - certificate = aws_acmpca_private_certificate.subordinate.certificate - certificate_chain = aws_acmpca_private_certificate.subordinate.certificate_chain + certificate = aws_acmpca_certificate.subordinate.certificate + certificate_chain = aws_acmpca_certificate.subordinate.certificate_chain } -resource "aws_acmpca_private_certificate" "subordinate" { +resource "aws_acmpca_certificate" "subordinate" { certificate_authority_arn = aws_acmpca_certificate_authority.root.arn certificate_signing_request = aws_acmpca_certificate_authority.subordinate.certificate_signing_request signing_algorithm = "SHA512WITHRSA" @@ -93,7 +93,7 @@ resource "aws_acmpca_certificate_authority_certificate" "root" { # ... } -resource "aws_acmpca_private_certificate" "root" { +resource "aws_acmpca_certificate" "root" { # ... } From 35ae85b4aa2a8d67c73030398c9183a63c3e9216 Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Thu, 25 Feb 2021 09:51:14 -0800 Subject: [PATCH 25/31] Add back test for end entity certificate and test updating root CA --- ...data_source_aws_acmpca_certificate_test.go | 2 +- aws/resource_aws_acmpca_certificate.go | 15 ++- ..._certificate_authority_certificate_test.go | 71 +++++++++++ aws/resource_aws_acmpca_certificate_test.go | 111 +++++++++++------- .../docs/r/acmpca_certificate.html.markdown | 7 -- 5 files changed, 145 insertions(+), 61 deletions(-) diff --git a/aws/data_source_aws_acmpca_certificate_test.go b/aws/data_source_aws_acmpca_certificate_test.go index ab222c2de0d4..c000be001512 100644 --- a/aws/data_source_aws_acmpca_certificate_test.go +++ b/aws/data_source_aws_acmpca_certificate_test.go @@ -20,7 +20,7 @@ func TestAccDataSourceAwsAcmpcaCertificate_Basic(t *testing.T) { Steps: []resource.TestStep{ { Config: testAccDataSourceAwsAcmpcaCertificateConfig_NonExistent, - ExpectError: regexp.MustCompile(`(AccessDeniedException|ResourceNotFoundException)`), + ExpectError: regexp.MustCompile(`ResourceNotFoundException`), }, { Config: testAccDataSourceAwsAcmpcaCertificateConfig_ARN(rName), diff --git a/aws/resource_aws_acmpca_certificate.go b/aws/resource_aws_acmpca_certificate.go index 0969a0621b6a..72c7d12c64d5 100644 --- a/aws/resource_aws_acmpca_certificate.go +++ b/aws/resource_aws_acmpca_certificate.go @@ -46,10 +46,9 @@ func resourceAwsAcmpcaCertificate() *schema.Resource { ValidateFunc: validateArn, }, "certificate_signing_request": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - StateFunc: normalizeCert, + Type: schema.TypeString, + Required: true, + ForceNew: true, }, "signing_algorithm": { Type: schema.TypeString, @@ -144,7 +143,7 @@ func resourceAwsAcmpcaCertificateRead(d *schema.ResourceData, meta interface{}) certificateOutput, err := conn.GetCertificate(getCertificateInput) if err != nil { if isAWSErr(err, acmpca.ErrCodeResourceNotFoundException, "") { - log.Printf("[WARN] ACM PCA Certificate %q not found - removing from state", d.Id()) + log.Printf("[WARN] ACM PCA Certificate (%s) not found, removing from state", d.Id()) d.SetId("") return nil } @@ -163,12 +162,12 @@ func resourceAwsAcmpcaCertificateRevoke(d *schema.ResourceData, meta interface{} block, _ := pem.Decode([]byte(d.Get("certificate").(string))) if block == nil { - log.Printf("[WARN] Failed to parse certificate %q", d.Id()) + log.Printf("[WARN] Failed to parse ACM PCA Certificate (%s)", d.Id()) return nil } cert, err := x509.ParseCertificate(block.Bytes) if err != nil { - return fmt.Errorf("Failed to parse ACM PCA Certificate: %s", err) + return fmt.Errorf("Failed to parse ACM PCA Certificate (%s): %w", d.Id(), err) } input := &acmpca.RevokeCertificateInput{ @@ -185,7 +184,7 @@ func resourceAwsAcmpcaCertificateRevoke(d *schema.ResourceData, meta interface{} return nil } if err != nil { - return fmt.Errorf("error revoking ACM PCA Certificate: %s", err) + return fmt.Errorf("error revoking ACM PCA Certificate (%s): %w", d.Id(), err) } return nil diff --git a/aws/resource_aws_acmpca_certificate_authority_certificate_test.go b/aws/resource_aws_acmpca_certificate_authority_certificate_test.go index 5dd42cc6f581..afc96ae6ef53 100644 --- a/aws/resource_aws_acmpca_certificate_authority_certificate_test.go +++ b/aws/resource_aws_acmpca_certificate_authority_certificate_test.go @@ -35,6 +35,39 @@ func TestAccAwsAcmpcaCertificateAuthorityCertificate_RootCA(t *testing.T) { }) } +func TestAccAwsAcmpcaCertificateAuthorityCertificate_UpdateRootCA(t *testing.T) { + var v acmpca.GetCertificateAuthorityCertificateOutput + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_acmpca_certificate_authority_certificate.test" + updatedResourceName := "aws_acmpca_certificate_authority_certificate.updated" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: nil, // Certificate authority certificates cannot be deleted + Steps: []resource.TestStep{ + { + Config: testAccAwsAcmpcaCertificateAuthorityCertificate_RootCA(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAwsAcmpcaCertificateAuthorityCertificateExists(resourceName, &v), + resource.TestCheckResourceAttrPair(resourceName, "certificate_authority_arn", "aws_acmpca_certificate_authority.test", "arn"), + resource.TestCheckResourceAttrPair(resourceName, "certificate", "aws_acmpca_certificate.test", "certificate"), + resource.TestCheckResourceAttrPair(resourceName, "certificate_chain", "aws_acmpca_certificate.test", "certificate_chain"), + ), + }, + { + Config: testAccAwsAcmpcaCertificateAuthorityCertificate_UpdateRootCA(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAwsAcmpcaCertificateAuthorityCertificateExists(updatedResourceName, &v), + resource.TestCheckResourceAttrPair(updatedResourceName, "certificate_authority_arn", "aws_acmpca_certificate_authority.test", "arn"), + resource.TestCheckResourceAttrPair(updatedResourceName, "certificate", "aws_acmpca_certificate.updated", "certificate"), + resource.TestCheckResourceAttrPair(updatedResourceName, "certificate_chain", "aws_acmpca_certificate.updated", "certificate_chain"), + ), + }, + }, + }) +} + func TestAccAwsAcmpcaCertificateAuthorityCertificate_SubordinateCA(t *testing.T) { var v acmpca.GetCertificateAuthorityCertificateOutput rName := acctest.RandomWithPrefix("tf-acc-test") @@ -119,6 +152,44 @@ data "aws_partition" "current" {} `, rName) } +func testAccAwsAcmpcaCertificateAuthorityCertificate_UpdateRootCA(rName string) string { + return fmt.Sprintf(` +resource "aws_acmpca_certificate_authority_certificate" "updated" { + certificate_authority_arn = aws_acmpca_certificate_authority.test.arn + + certificate = aws_acmpca_certificate.updated.certificate + certificate_chain = aws_acmpca_certificate.updated.certificate_chain +} + +resource "aws_acmpca_certificate" "updated" { + 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_length = 1 + validity_unit = "YEARS" +} + +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]s.com" + } + } +} + +data "aws_partition" "current" {} +`, rName) +} + func testAccAwsAcmpcaCertificateAuthorityCertificate_SubordinateCA(rName string) string { return fmt.Sprintf(` resource "aws_acmpca_certificate_authority_certificate" "test" { diff --git a/aws/resource_aws_acmpca_certificate_test.go b/aws/resource_aws_acmpca_certificate_test.go index 67775c9262f3..953c2ff978b1 100644 --- a/aws/resource_aws_acmpca_certificate_test.go +++ b/aws/resource_aws_acmpca_certificate_test.go @@ -13,11 +13,10 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" ) -func TestAccAwsAcmpcaCertificate_Basic(t *testing.T) { - t.Skip("Non-root certificates not yet supported") - +func TestAccAwsAcmpcaCertificate_RootCertificate(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_acmpca_certificate.test" - csr, _ := tlsRsaX509CertificateRequestPem(4096, "terraformtest1.com") + certificateAuthorityResourceName := "aws_acmpca_certificate_authority.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -25,27 +24,28 @@ func TestAccAwsAcmpcaCertificate_Basic(t *testing.T) { CheckDestroy: testAccCheckAwsAcmpcaCertificateDestroy, Steps: []resource.TestStep{ { - Config: testAccAwsAcmpcaCertificateConfig_Basic(tlsPemEscapeNewlines(csr)), + Config: testAccAwsAcmpcaCertificateConfig_RootCertificate(rName), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAwsAcmpcaCertificateExists(resourceName), testAccMatchResourceAttrRegionalARN(resourceName, "arn", "acm-pca", regexp.MustCompile(`certificate-authority/.+/certificate/.+$`)), resource.TestCheckResourceAttrSet(resourceName, "certificate"), - resource.TestCheckResourceAttrSet(resourceName, "certificate_chain"), - resource.TestCheckResourceAttr(resourceName, "certificate_signing_request", csr), + resource.TestCheckResourceAttr(resourceName, "certificate_chain", ""), + resource.TestCheckResourceAttrPair(resourceName, "certificate_authority_arn", certificateAuthorityResourceName, "arn"), + resource.TestCheckResourceAttrSet(resourceName, "certificate_signing_request"), resource.TestCheckResourceAttr(resourceName, "validity_length", "1"), resource.TestCheckResourceAttr(resourceName, "validity_unit", "YEARS"), - resource.TestCheckResourceAttr(resourceName, "signing_algorithm", "SHA256WITHRSA"), - testAccCheckResourceAttrGlobalARNNoAccount(resourceName, "template_arn", "acm-pca", "template/EndEntityCertificate/V1"), + resource.TestCheckResourceAttr(resourceName, "signing_algorithm", "SHA512WITHRSA"), + testAccCheckResourceAttrGlobalARNNoAccount(resourceName, "template_arn", "acm-pca", "template/RootCACertificate/V1"), ), }, }, }) } -func TestAccAwsAcmpcaCertificate_RootCertificate(t *testing.T) { +func TestAccAwsAcmpcaCertificate_EndEntityCertificate(t *testing.T) { rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_acmpca_certificate.test" - certificateAuthorityResourceName := "aws_acmpca_certificate_authority.test" + csr, _ := tlsRsaX509CertificateRequestPem(4096, "terraformtest1.com") resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -53,18 +53,17 @@ func TestAccAwsAcmpcaCertificate_RootCertificate(t *testing.T) { CheckDestroy: testAccCheckAwsAcmpcaCertificateDestroy, Steps: []resource.TestStep{ { - Config: testAccAwsAcmpcaCertificateConfig_RootCertificate(rName), + Config: testAccAwsAcmpcaCertificateConfig_EndEntityCertificate(rName, tlsPemEscapeNewlines(csr)), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAwsAcmpcaCertificateExists(resourceName), testAccMatchResourceAttrRegionalARN(resourceName, "arn", "acm-pca", regexp.MustCompile(`certificate-authority/.+/certificate/.+$`)), resource.TestCheckResourceAttrSet(resourceName, "certificate"), - resource.TestCheckResourceAttr(resourceName, "certificate_chain", ""), - resource.TestCheckResourceAttrPair(resourceName, "certificate_authority_arn", certificateAuthorityResourceName, "arn"), - resource.TestCheckResourceAttrSet(resourceName, "certificate_signing_request"), + resource.TestCheckResourceAttrSet(resourceName, "certificate_chain"), + resource.TestCheckResourceAttr(resourceName, "certificate_signing_request", csr), resource.TestCheckResourceAttr(resourceName, "validity_length", "1"), - resource.TestCheckResourceAttr(resourceName, "validity_unit", "YEARS"), - resource.TestCheckResourceAttr(resourceName, "signing_algorithm", "SHA512WITHRSA"), - testAccCheckResourceAttrGlobalARNNoAccount(resourceName, "template_arn", "acm-pca", "template/RootCACertificate/V1"), + resource.TestCheckResourceAttr(resourceName, "validity_unit", "DAYS"), + resource.TestCheckResourceAttr(resourceName, "signing_algorithm", "SHA256WITHRSA"), + testAccCheckResourceAttrGlobalARNNoAccount(resourceName, "template_arn", "acm-pca", "template/EndEntityCertificate/V1"), ), }, }, @@ -131,33 +130,6 @@ func testAccCheckAwsAcmpcaCertificateExists(resourceName string) resource.TestCh } } -// nolint:unused -func testAccAwsAcmpcaCertificateConfig_Basic(csr string) string { - return fmt.Sprintf(` -resource "aws_acmpca_certificate" "test" { - certificate_authority_arn = aws_acmpca_certificate_authority.test.arn - certificate_signing_request = "%[1]s" - signing_algorithm = "SHA256WITHRSA" - validity_length = 1 - validity_unit = "YEARS" -} - -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 = "terraformtesting.com" - } - } -} -`, csr) -} - func testAccAwsAcmpcaCertificateConfig_RootCertificate(rName string) string { return fmt.Sprintf(` resource "aws_acmpca_certificate" "test" { @@ -189,6 +161,55 @@ data "aws_partition" "current" {} `, rName) } +func testAccAwsAcmpcaCertificateConfig_EndEntityCertificate(rName, csr string) string { + return fmt.Sprintf(` +resource "aws_acmpca_certificate" "test" { + certificate_authority_arn = aws_acmpca_certificate_authority.root.arn + certificate_signing_request = "%[2]s" + signing_algorithm = "SHA256WITHRSA" + + template_arn = "arn:${data.aws_partition.current.partition}:acm-pca:::template/EndEntityCertificate/V1" + + validity_length = 1 + validity_unit = "DAYS" +} + +resource "aws_acmpca_certificate_authority" "root" { + permanent_deletion_time_in_days = 7 + type = "ROOT" + + certificate_authority_configuration { + key_algorithm = "RSA_4096" + signing_algorithm = "SHA512WITHRSA" + + subject { + common_name = "%[1]s.com" + } + } + } + + resource "aws_acmpca_certificate_authority_certificate" "root" { + certificate_authority_arn = aws_acmpca_certificate_authority.root.arn + + certificate = aws_acmpca_certificate.root.certificate + certificate_chain = aws_acmpca_certificate.root.certificate_chain + } + + resource "aws_acmpca_certificate" "root" { + certificate_authority_arn = aws_acmpca_certificate_authority.root.arn + certificate_signing_request = aws_acmpca_certificate_authority.root.certificate_signing_request + signing_algorithm = "SHA512WITHRSA" + + template_arn = "arn:${data.aws_partition.current.partition}:acm-pca:::template/RootCACertificate/V1" + + validity_length = 2 + validity_unit = "YEARS" + } + + data "aws_partition" "current" {} + `, rName, csr) +} + func TestValidateAcmPcaTemplateArn(t *testing.T) { validNames := []string{ "arn:aws:acm-pca:::template/EndEntityCertificate/V1", // lintignore:AWSAT005 diff --git a/website/docs/r/acmpca_certificate.html.markdown b/website/docs/r/acmpca_certificate.html.markdown index 8a9ef19e60a7..1e5d98e102d3 100644 --- a/website/docs/r/acmpca_certificate.html.markdown +++ b/website/docs/r/acmpca_certificate.html.markdown @@ -70,13 +70,6 @@ In addition to all arguments above, the following attributes are exported: * `certificate` - The PEM-encoded certificate value. * `certificate_chain` - The PEM-encoded certificate chain that includes any intermediate certificates and chains up to root CA. -## Timeouts - -`aws_acmpca_certificate` provides the following [Timeouts](/docs/configuration/resources.html#timeouts) -configuration options: - -* `create` - (Default `5m`) How long to wait for a certificate authority to issue a certificate. - ## Import `aws_acmpca_certificate` can not be imported at this time. From 07fe7c60b976e2f47b9eccc430f7689f6704ebdd Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Thu, 25 Feb 2021 12:14:44 -0800 Subject: [PATCH 26/31] Adds test for issuing subordinate CA certificate --- aws/resource_aws_acmpca_certificate_test.go | 93 +++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/aws/resource_aws_acmpca_certificate_test.go b/aws/resource_aws_acmpca_certificate_test.go index 953c2ff978b1..1ff1e2547756 100644 --- a/aws/resource_aws_acmpca_certificate_test.go +++ b/aws/resource_aws_acmpca_certificate_test.go @@ -42,6 +42,36 @@ func TestAccAwsAcmpcaCertificate_RootCertificate(t *testing.T) { }) } +func TestAccAwsAcmpcaCertificate_SubordinateCertificate(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_acmpca_certificate.test" + rootCertificateAuthorityResourceName := "aws_acmpca_certificate_authority.root" + subordinateCertificateAuthorityResourceName := "aws_acmpca_certificate_authority.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsAcmpcaCertificateDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsAcmpcaCertificateConfig_SubordinateCertificate(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAwsAcmpcaCertificateExists(resourceName), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "acm-pca", regexp.MustCompile(`certificate-authority/.+/certificate/.+$`)), + resource.TestCheckResourceAttrSet(resourceName, "certificate"), + resource.TestCheckResourceAttrSet(resourceName, "certificate_chain"), + resource.TestCheckResourceAttrPair(resourceName, "certificate_authority_arn", rootCertificateAuthorityResourceName, "arn"), + resource.TestCheckResourceAttrPair(resourceName, "certificate_signing_request", subordinateCertificateAuthorityResourceName, "certificate_signing_request"), + resource.TestCheckResourceAttr(resourceName, "validity_length", "1"), + resource.TestCheckResourceAttr(resourceName, "validity_unit", "YEARS"), + resource.TestCheckResourceAttr(resourceName, "signing_algorithm", "SHA512WITHRSA"), + testAccCheckResourceAttrGlobalARNNoAccount(resourceName, "template_arn", "acm-pca", "template/SubordinateCACertificate_PathLen0/V1"), + ), + }, + }, + }) +} + func TestAccAwsAcmpcaCertificate_EndEntityCertificate(t *testing.T) { rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_acmpca_certificate.test" @@ -161,6 +191,69 @@ data "aws_partition" "current" {} `, rName) } +func testAccAwsAcmpcaCertificateConfig_SubordinateCertificate(rName string) string { + return fmt.Sprintf(` +resource "aws_acmpca_certificate" "test" { + certificate_authority_arn = aws_acmpca_certificate_authority.root.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/SubordinateCACertificate_PathLen0/V1" + + validity_length = 1 + validity_unit = "YEARS" +} + +resource "aws_acmpca_certificate_authority" "test" { + permanent_deletion_time_in_days = 7 + type = "SUBORDINATE" + + certificate_authority_configuration { + key_algorithm = "RSA_2048" + signing_algorithm = "SHA512WITHRSA" + + subject { + common_name = "sub.%[1]s.com" + } + } +} + +resource "aws_acmpca_certificate_authority" "root" { + permanent_deletion_time_in_days = 7 + type = "ROOT" + + certificate_authority_configuration { + key_algorithm = "RSA_4096" + signing_algorithm = "SHA512WITHRSA" + + subject { + common_name = "%[1]s.com" + } + } +} + +resource "aws_acmpca_certificate_authority_certificate" "root" { + certificate_authority_arn = aws_acmpca_certificate_authority.root.arn + + certificate = aws_acmpca_certificate.root.certificate + certificate_chain = aws_acmpca_certificate.root.certificate_chain +} + +resource "aws_acmpca_certificate" "root" { + certificate_authority_arn = aws_acmpca_certificate_authority.root.arn + certificate_signing_request = aws_acmpca_certificate_authority.root.certificate_signing_request + signing_algorithm = "SHA512WITHRSA" + + template_arn = "arn:${data.aws_partition.current.partition}:acm-pca:::template/RootCACertificate/V1" + + validity_length = 2 + validity_unit = "YEARS" +} + +data "aws_partition" "current" {} +`, rName) +} + func testAccAwsAcmpcaCertificateConfig_EndEntityCertificate(rName, csr string) string { return fmt.Sprintf(` resource "aws_acmpca_certificate" "test" { From 78ec08ed4e89f3eb581272234c26e1894b1a85da Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Thu, 25 Feb 2021 13:58:56 -0800 Subject: [PATCH 27/31] Adds import to certificate --- aws/resource_aws_acmpca_certificate.go | 16 +++++++-- aws/resource_aws_acmpca_certificate_test.go | 36 +++++++++++++++++++++ 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/aws/resource_aws_acmpca_certificate.go b/aws/resource_aws_acmpca_certificate.go index 72c7d12c64d5..43756eab1ff7 100644 --- a/aws/resource_aws_acmpca_certificate.go +++ b/aws/resource_aws_acmpca_certificate.go @@ -5,6 +5,7 @@ import ( "encoding/pem" "fmt" "log" + "regexp" "strings" "time" @@ -22,8 +23,19 @@ func resourceAwsAcmpcaCertificate() *schema.Resource { Create: resourceAwsAcmpcaCertificateCreate, Read: resourceAwsAcmpcaCertificateRead, Delete: resourceAwsAcmpcaCertificateRevoke, - Timeouts: &schema.ResourceTimeout{ - Create: schema.DefaultTimeout(5 * time.Minute), + + Importer: &schema.ResourceImporter{ + State: func(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + re := regexp.MustCompile(`arn:.+:certificate-authority/[^/]+`) + authorityArn := re.FindString(d.Id()) + if authorityArn == "" { + return nil, fmt.Errorf("Unexpected format for ID (%q), expected ACM PCA Certificate ARN", d.Id()) + } + + d.Set("certificate_authority_arn", authorityArn) + + return []*schema.ResourceData{d}, nil + }, }, Schema: map[string]*schema.Schema{ diff --git a/aws/resource_aws_acmpca_certificate_test.go b/aws/resource_aws_acmpca_certificate_test.go index 1ff1e2547756..6aba73306b3f 100644 --- a/aws/resource_aws_acmpca_certificate_test.go +++ b/aws/resource_aws_acmpca_certificate_test.go @@ -38,6 +38,18 @@ func TestAccAwsAcmpcaCertificate_RootCertificate(t *testing.T) { testAccCheckResourceAttrGlobalARNNoAccount(resourceName, "template_arn", "acm-pca", "template/RootCACertificate/V1"), ), }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "certificate_signing_request", + "signing_algorithm", + "template_arn", + "validity_length", + "validity_unit", + }, + }, }, }) } @@ -68,6 +80,18 @@ func TestAccAwsAcmpcaCertificate_SubordinateCertificate(t *testing.T) { testAccCheckResourceAttrGlobalARNNoAccount(resourceName, "template_arn", "acm-pca", "template/SubordinateCACertificate_PathLen0/V1"), ), }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "certificate_signing_request", + "signing_algorithm", + "template_arn", + "validity_length", + "validity_unit", + }, + }, }, }) } @@ -96,6 +120,18 @@ func TestAccAwsAcmpcaCertificate_EndEntityCertificate(t *testing.T) { testAccCheckResourceAttrGlobalARNNoAccount(resourceName, "template_arn", "acm-pca", "template/EndEntityCertificate/V1"), ), }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "certificate_signing_request", + "signing_algorithm", + "template_arn", + "validity_length", + "validity_unit", + }, + }, }, }) } From 5f3436c371677f75bf334216b540ade3ef6912c3 Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Fri, 26 Feb 2021 16:48:05 -0800 Subject: [PATCH 28/31] Refactors certificate validity parameters to match API --- ...data_source_aws_acmpca_certificate_test.go | 6 +- aws/internal/service/acmpca/waiter/waiter.go | 4 + aws/resource_aws_acmpca_certificate.go | 75 ++++- ..._certificate_authority_certificate_test.go | 24 +- ...e_aws_acmpca_certificate_authority_test.go | 6 +- aws/resource_aws_acmpca_certificate_test.go | 274 +++++++++++++----- aws/resource_aws_guardduty_filter.go | 20 +- aws/validators.go | 5 + aws/validators_test.go | 29 ++ .../docs/r/acmpca_certificate.html.markdown | 14 +- ...ficate_authority_certificate.html.markdown | 12 +- 11 files changed, 352 insertions(+), 117 deletions(-) diff --git a/aws/data_source_aws_acmpca_certificate_test.go b/aws/data_source_aws_acmpca_certificate_test.go index c000be001512..e910995ddf3d 100644 --- a/aws/data_source_aws_acmpca_certificate_test.go +++ b/aws/data_source_aws_acmpca_certificate_test.go @@ -49,8 +49,10 @@ resource "aws_acmpca_certificate" "test" { template_arn = "arn:${data.aws_partition.current.partition}:acm-pca:::template/RootCACertificate/V1" - validity_length = 1 - validity_unit = "YEARS" + validity { + type = "YEARS" + value = 1 + } } resource "aws_acmpca_certificate_authority" "test" { diff --git a/aws/internal/service/acmpca/waiter/waiter.go b/aws/internal/service/acmpca/waiter/waiter.go index c9c4c803f9d7..0205dabec2ac 100644 --- a/aws/internal/service/acmpca/waiter/waiter.go +++ b/aws/internal/service/acmpca/waiter/waiter.go @@ -24,3 +24,7 @@ func CertificateAuthorityCreated(conn *acmpca.ACMPCA, arn string, timeout time.D return nil, err } + +const ( + CertificateAuthorityActiveTimeout = 1 * time.Minute +) diff --git a/aws/resource_aws_acmpca_certificate.go b/aws/resource_aws_acmpca_certificate.go index 43756eab1ff7..5a6317d0ec47 100644 --- a/aws/resource_aws_acmpca_certificate.go +++ b/aws/resource_aws_acmpca_certificate.go @@ -6,6 +6,7 @@ import ( "fmt" "log" "regexp" + "strconv" "strings" "time" @@ -16,6 +17,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/acmpca/waiter" ) func resourceAwsAcmpcaCertificate() *schema.Resource { @@ -68,16 +70,28 @@ func resourceAwsAcmpcaCertificate() *schema.Resource { ForceNew: true, ValidateFunc: validation.StringInSlice(acmpca.SigningAlgorithm_Values(), false), }, - "validity_length": { - Type: schema.TypeInt, + "validity": { + Type: schema.TypeList, Required: true, ForceNew: true, - }, - "validity_unit": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.StringInSlice(acmpca.ValidityPeriodType_Values(), false), + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "type": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice(acmpca.ValidityPeriodType_Values(), false), + }, + "value": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateTypeStringIsDateOrPositiveInt, + }, + }, + }, }, "template_arn": { Type: schema.TypeString, @@ -98,17 +112,19 @@ func resourceAwsAcmpcaCertificateCreate(d *schema.ResourceData, meta interface{} Csr: []byte(d.Get("certificate_signing_request").(string)), IdempotencyToken: aws.String(resource.UniqueId()), SigningAlgorithm: aws.String(d.Get("signing_algorithm").(string)), - Validity: &acmpca.Validity{ - Type: aws.String(d.Get("validity_unit").(string)), - Value: aws.Int64(int64(d.Get("validity_length").(int))), - }, } + validity, err := expandAcmpcaValidity(d.Get("validity").([]interface{})) + if err != nil { + return fmt.Errorf("error issuing ACM PCA Certificate with Certificate Authority (%s): %w", certificateAuthorityArn, err) + } + input.Validity = validity + if v, ok := d.Get("template_arn").(string); ok && v != "" { input.TemplateArn = aws.String(v) } var output *acmpca.IssueCertificateOutput - err := resource.Retry(5*time.Minute, func() *resource.RetryError { + err = resource.Retry(waiter.CertificateAuthorityActiveTimeout, func() *resource.RetryError { var err error output, err = conn.IssueCertificate(input) if tfawserr.ErrMessageContains(err, acmpca.ErrCodeInvalidStateException, "The certificate authority is not in a valid state for issuing certificates") { @@ -230,3 +246,36 @@ func validateAcmPcaTemplateArn(v interface{}, k string) (ws []string, errors []e return ws, errors } + +func expandAcmpcaValidity(l []interface{}) (*acmpca.Validity, error) { + if len(l) == 0 { + return nil, nil + } + + m := l[0].(map[string]interface{}) + + valueType := m["type"].(string) + result := &acmpca.Validity{ + Type: aws.String(valueType), + } + + i, err := expandAcmpcaValidityValue(valueType, m["value"].(string)) + if err != nil { + return nil, fmt.Errorf("error parsing value %q: %w", m["value"].(string), err) + } + result.Value = aws.Int64(i) + + return result, nil +} + +func expandAcmpcaValidityValue(valueType, v string) (int64, error) { + if valueType == acmpca.ValidityPeriodTypeEndDate { + date, err := time.Parse(time.RFC3339, v) + if err != nil { + return 0, err + } + v = date.UTC().Format("20060102150405") // YYYYMMDDHHMMSS + } + + return strconv.ParseInt(v, 10, 64) +} diff --git a/aws/resource_aws_acmpca_certificate_authority_certificate_test.go b/aws/resource_aws_acmpca_certificate_authority_certificate_test.go index afc96ae6ef53..b9af499cd56c 100644 --- a/aws/resource_aws_acmpca_certificate_authority_certificate_test.go +++ b/aws/resource_aws_acmpca_certificate_authority_certificate_test.go @@ -130,8 +130,10 @@ resource "aws_acmpca_certificate" "test" { template_arn = "arn:${data.aws_partition.current.partition}:acm-pca:::template/RootCACertificate/V1" - validity_length = 1 - validity_unit = "YEARS" + validity { + type = "YEARS" + value = 1 + } } resource "aws_acmpca_certificate_authority" "test" { @@ -168,8 +170,10 @@ resource "aws_acmpca_certificate" "updated" { template_arn = "arn:${data.aws_partition.current.partition}:acm-pca:::template/RootCACertificate/V1" - validity_length = 1 - validity_unit = "YEARS" + validity { + type = "YEARS" + value = 1 + } } resource "aws_acmpca_certificate_authority" "test" { @@ -206,8 +210,10 @@ resource "aws_acmpca_certificate" "test" { template_arn = "arn:${data.aws_partition.current.partition}:acm-pca:::template/SubordinateCACertificate_PathLen0/V1" - validity_length = 1 - validity_unit = "YEARS" + validity { + type = "YEARS" + value = 1 + } } resource "aws_acmpca_certificate_authority" "test" { @@ -252,8 +258,10 @@ resource "aws_acmpca_certificate" "root" { template_arn = "arn:${data.aws_partition.current.partition}:acm-pca:::template/RootCACertificate/V1" - validity_length = 2 - validity_unit = "YEARS" + validity { + type = "YEARS" + value = 2 + } } data "aws_partition" "current" {} diff --git a/aws/resource_aws_acmpca_certificate_authority_test.go b/aws/resource_aws_acmpca_certificate_authority_test.go index 30aac6fc17b5..031d2a974ee0 100644 --- a/aws/resource_aws_acmpca_certificate_authority_test.go +++ b/aws/resource_aws_acmpca_certificate_authority_test.go @@ -682,8 +682,10 @@ resource "aws_acmpca_certificate" "test" { template_arn = "arn:${data.aws_partition.current.partition}:acm-pca:::template/RootCACertificate/V1" - validity_length = 1 - validity_unit = "YEARS" + validity { + type = "YEARS" + value = 1 + } } data "aws_partition" "current" {} diff --git a/aws/resource_aws_acmpca_certificate_test.go b/aws/resource_aws_acmpca_certificate_test.go index 6aba73306b3f..516502483ccb 100644 --- a/aws/resource_aws_acmpca_certificate_test.go +++ b/aws/resource_aws_acmpca_certificate_test.go @@ -3,7 +3,9 @@ package aws import ( "fmt" "regexp" + "strconv" "testing" + "time" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/acmpca" @@ -32,8 +34,8 @@ func TestAccAwsAcmpcaCertificate_RootCertificate(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "certificate_chain", ""), resource.TestCheckResourceAttrPair(resourceName, "certificate_authority_arn", certificateAuthorityResourceName, "arn"), resource.TestCheckResourceAttrSet(resourceName, "certificate_signing_request"), - resource.TestCheckResourceAttr(resourceName, "validity_length", "1"), - resource.TestCheckResourceAttr(resourceName, "validity_unit", "YEARS"), + resource.TestCheckResourceAttr(resourceName, "validity.0.value", "1"), + resource.TestCheckResourceAttr(resourceName, "validity.0.type", "YEARS"), resource.TestCheckResourceAttr(resourceName, "signing_algorithm", "SHA512WITHRSA"), testAccCheckResourceAttrGlobalARNNoAccount(resourceName, "template_arn", "acm-pca", "template/RootCACertificate/V1"), ), @@ -46,8 +48,7 @@ func TestAccAwsAcmpcaCertificate_RootCertificate(t *testing.T) { "certificate_signing_request", "signing_algorithm", "template_arn", - "validity_length", - "validity_unit", + "validity", }, }, }, @@ -74,8 +75,8 @@ func TestAccAwsAcmpcaCertificate_SubordinateCertificate(t *testing.T) { resource.TestCheckResourceAttrSet(resourceName, "certificate_chain"), resource.TestCheckResourceAttrPair(resourceName, "certificate_authority_arn", rootCertificateAuthorityResourceName, "arn"), resource.TestCheckResourceAttrPair(resourceName, "certificate_signing_request", subordinateCertificateAuthorityResourceName, "certificate_signing_request"), - resource.TestCheckResourceAttr(resourceName, "validity_length", "1"), - resource.TestCheckResourceAttr(resourceName, "validity_unit", "YEARS"), + resource.TestCheckResourceAttr(resourceName, "validity.0.value", "1"), + resource.TestCheckResourceAttr(resourceName, "validity.0.type", "YEARS"), resource.TestCheckResourceAttr(resourceName, "signing_algorithm", "SHA512WITHRSA"), testAccCheckResourceAttrGlobalARNNoAccount(resourceName, "template_arn", "acm-pca", "template/SubordinateCACertificate_PathLen0/V1"), ), @@ -88,8 +89,7 @@ func TestAccAwsAcmpcaCertificate_SubordinateCertificate(t *testing.T) { "certificate_signing_request", "signing_algorithm", "template_arn", - "validity_length", - "validity_unit", + "validity", }, }, }, @@ -114,8 +114,8 @@ func TestAccAwsAcmpcaCertificate_EndEntityCertificate(t *testing.T) { resource.TestCheckResourceAttrSet(resourceName, "certificate"), resource.TestCheckResourceAttrSet(resourceName, "certificate_chain"), resource.TestCheckResourceAttr(resourceName, "certificate_signing_request", csr), - resource.TestCheckResourceAttr(resourceName, "validity_length", "1"), - resource.TestCheckResourceAttr(resourceName, "validity_unit", "DAYS"), + resource.TestCheckResourceAttr(resourceName, "validity.0.value", "1"), + resource.TestCheckResourceAttr(resourceName, "validity.0.type", "DAYS"), resource.TestCheckResourceAttr(resourceName, "signing_algorithm", "SHA256WITHRSA"), testAccCheckResourceAttrGlobalARNNoAccount(resourceName, "template_arn", "acm-pca", "template/EndEntityCertificate/V1"), ), @@ -128,8 +128,87 @@ func TestAccAwsAcmpcaCertificate_EndEntityCertificate(t *testing.T) { "certificate_signing_request", "signing_algorithm", "template_arn", - "validity_length", - "validity_unit", + "validity", + }, + }, + }, + }) +} + +func TestAccAwsAcmpcaCertificate_Validity_EndDate(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_acmpca_certificate.test" + csr, _ := tlsRsaX509CertificateRequestPem(4096, "terraformtest1.com") + later := time.Now().Add(time.Minute * 10).Format(time.RFC3339) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsAcmpcaCertificateDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsAcmpcaCertificateConfig_Validity_EndDate(rName, tlsPemEscapeNewlines(csr), later), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAwsAcmpcaCertificateExists(resourceName), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "acm-pca", regexp.MustCompile(`certificate-authority/.+/certificate/.+$`)), + resource.TestCheckResourceAttrSet(resourceName, "certificate"), + resource.TestCheckResourceAttrSet(resourceName, "certificate_chain"), + resource.TestCheckResourceAttr(resourceName, "certificate_signing_request", csr), + resource.TestCheckResourceAttr(resourceName, "validity.0.value", later), + resource.TestCheckResourceAttr(resourceName, "validity.0.type", "END_DATE"), + resource.TestCheckResourceAttr(resourceName, "signing_algorithm", "SHA256WITHRSA"), + testAccCheckResourceAttrGlobalARNNoAccount(resourceName, "template_arn", "acm-pca", "template/EndEntityCertificate/V1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "certificate_signing_request", + "signing_algorithm", + "template_arn", + "validity", + }, + }, + }, + }) +} + +func TestAccAwsAcmpcaCertificate_Validity_Absolute(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_acmpca_certificate.test" + csr, _ := tlsRsaX509CertificateRequestPem(4096, "terraformtest1.com") + later := time.Now().Add(time.Minute * 10).Unix() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsAcmpcaCertificateDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsAcmpcaCertificateConfig_Validity_Absolute(rName, tlsPemEscapeNewlines(csr), later), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAwsAcmpcaCertificateExists(resourceName), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "acm-pca", regexp.MustCompile(`certificate-authority/.+/certificate/.+$`)), + resource.TestCheckResourceAttrSet(resourceName, "certificate"), + resource.TestCheckResourceAttrSet(resourceName, "certificate_chain"), + resource.TestCheckResourceAttr(resourceName, "certificate_signing_request", csr), + resource.TestCheckResourceAttr(resourceName, "validity.0.value", strconv.FormatInt(later, 10)), + resource.TestCheckResourceAttr(resourceName, "validity.0.type", "ABSOLUTE"), + resource.TestCheckResourceAttr(resourceName, "signing_algorithm", "SHA256WITHRSA"), + testAccCheckResourceAttrGlobalARNNoAccount(resourceName, "template_arn", "acm-pca", "template/EndEntityCertificate/V1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "certificate_signing_request", + "signing_algorithm", + "template_arn", + "validity", }, }, }, @@ -205,8 +284,10 @@ resource "aws_acmpca_certificate" "test" { template_arn = "arn:${data.aws_partition.current.partition}:acm-pca:::template/RootCACertificate/V1" - validity_length = 1 - validity_unit = "YEARS" + validity { + type = "YEARS" + value = 1 + } } resource "aws_acmpca_certificate_authority" "test" { @@ -228,7 +309,9 @@ data "aws_partition" "current" {} } func testAccAwsAcmpcaCertificateConfig_SubordinateCertificate(rName string) string { - return fmt.Sprintf(` + return composeConfig( + testAccAcmpcaCertificateBaseRootCAConfig(rName), + fmt.Sprintf(` resource "aws_acmpca_certificate" "test" { certificate_authority_arn = aws_acmpca_certificate_authority.root.arn certificate_signing_request = aws_acmpca_certificate_authority.test.certificate_signing_request @@ -236,8 +319,10 @@ resource "aws_acmpca_certificate" "test" { template_arn = "arn:${data.aws_partition.current.partition}:acm-pca:::template/SubordinateCACertificate_PathLen0/V1" - validity_length = 1 - validity_unit = "YEARS" + validity { + type = "YEARS" + value = 1 + } } resource "aws_acmpca_certificate_authority" "test" { @@ -253,7 +338,68 @@ resource "aws_acmpca_certificate_authority" "test" { } } } +`, rName)) +} + +func testAccAwsAcmpcaCertificateConfig_EndEntityCertificate(rName, csr string) string { + return composeConfig( + testAccAcmpcaCertificateBaseRootCAConfig(rName), + fmt.Sprintf(` +resource "aws_acmpca_certificate" "test" { + certificate_authority_arn = aws_acmpca_certificate_authority.root.arn + certificate_signing_request = "%[2]s" + signing_algorithm = "SHA256WITHRSA" + + template_arn = "arn:${data.aws_partition.current.partition}:acm-pca:::template/EndEntityCertificate/V1" + + validity { + type = "DAYS" + value = 1 + } +} +`, rName, csr)) +} +func testAccAwsAcmpcaCertificateConfig_Validity_EndDate(rName, csr, expiry string) string { + return composeConfig( + testAccAcmpcaCertificateBaseRootCAConfig(rName), + fmt.Sprintf(` +resource "aws_acmpca_certificate" "test" { + certificate_authority_arn = aws_acmpca_certificate_authority.root.arn + certificate_signing_request = "%[2]s" + signing_algorithm = "SHA256WITHRSA" + + template_arn = "arn:${data.aws_partition.current.partition}:acm-pca:::template/EndEntityCertificate/V1" + + validity { + type = "END_DATE" + value = %[3]q + } +} +`, rName, csr, expiry)) +} + +func testAccAwsAcmpcaCertificateConfig_Validity_Absolute(rName, csr string, expiry int64) string { + return composeConfig( + testAccAcmpcaCertificateBaseRootCAConfig(rName), + fmt.Sprintf(` +resource "aws_acmpca_certificate" "test" { + certificate_authority_arn = aws_acmpca_certificate_authority.root.arn + certificate_signing_request = "%[2]s" + signing_algorithm = "SHA256WITHRSA" + + template_arn = "arn:${data.aws_partition.current.partition}:acm-pca:::template/EndEntityCertificate/V1" + + validity { + type = "ABSOLUTE" + value = %[3]d + } +} +`, rName, csr, expiry)) +} + +func testAccAcmpcaCertificateBaseRootCAConfig(rName string) string { + return fmt.Sprintf(` resource "aws_acmpca_certificate_authority" "root" { permanent_deletion_time_in_days = 7 type = "ROOT" @@ -282,61 +428,14 @@ resource "aws_acmpca_certificate" "root" { template_arn = "arn:${data.aws_partition.current.partition}:acm-pca:::template/RootCACertificate/V1" - validity_length = 2 - validity_unit = "YEARS" + validity { + type = "YEARS" + value = 2 + } } data "aws_partition" "current" {} -`, rName) -} - -func testAccAwsAcmpcaCertificateConfig_EndEntityCertificate(rName, csr string) string { - return fmt.Sprintf(` -resource "aws_acmpca_certificate" "test" { - certificate_authority_arn = aws_acmpca_certificate_authority.root.arn - certificate_signing_request = "%[2]s" - signing_algorithm = "SHA256WITHRSA" - - template_arn = "arn:${data.aws_partition.current.partition}:acm-pca:::template/EndEntityCertificate/V1" - - validity_length = 1 - validity_unit = "DAYS" -} - -resource "aws_acmpca_certificate_authority" "root" { - permanent_deletion_time_in_days = 7 - type = "ROOT" - - certificate_authority_configuration { - key_algorithm = "RSA_4096" - signing_algorithm = "SHA512WITHRSA" - - subject { - common_name = "%[1]s.com" - } - } - } - - resource "aws_acmpca_certificate_authority_certificate" "root" { - certificate_authority_arn = aws_acmpca_certificate_authority.root.arn - - certificate = aws_acmpca_certificate.root.certificate - certificate_chain = aws_acmpca_certificate.root.certificate_chain - } - - resource "aws_acmpca_certificate" "root" { - certificate_authority_arn = aws_acmpca_certificate_authority.root.arn - certificate_signing_request = aws_acmpca_certificate_authority.root.certificate_signing_request - signing_algorithm = "SHA512WITHRSA" - - template_arn = "arn:${data.aws_partition.current.partition}:acm-pca:::template/RootCACertificate/V1" - - validity_length = 2 - validity_unit = "YEARS" - } - - data "aws_partition" "current" {} - `, rName, csr) + `, rName) } func TestValidateAcmPcaTemplateArn(t *testing.T) { @@ -367,3 +466,40 @@ func TestValidateAcmPcaTemplateArn(t *testing.T) { } } } + +func TestExpandAcmpcaValidityValue(t *testing.T) { + testCases := []struct { + Type string + Value string + Expected int64 + }{ + { + Type: acmpca.ValidityPeriodTypeEndDate, + Value: "2021-02-26T16:04:00Z", + Expected: 20210226160400, + }, + { + Type: acmpca.ValidityPeriodTypeEndDate, + Value: "2021-02-26T16:04:00-08:00", + Expected: 20210227000400, + }, + { + Type: acmpca.ValidityPeriodTypeAbsolute, + Value: "1614385420", + Expected: 1614385420, + }, + { + Type: acmpca.ValidityPeriodTypeYears, + Value: "2", + Expected: 2, + }, + } + + for _, testcase := range testCases { + i, _ := expandAcmpcaValidityValue(testcase.Type, testcase.Value) + if i != testcase.Expected { + t.Errorf("%s, %q: expected %d, got %d", testcase.Type, testcase.Value, testcase.Expected, i) + } + } + +} diff --git a/aws/resource_aws_guardduty_filter.go b/aws/resource_aws_guardduty_filter.go index aba50d68e6fd..283251001583 100644 --- a/aws/resource_aws_guardduty_filter.go +++ b/aws/resource_aws_guardduty_filter.go @@ -3,7 +3,6 @@ package aws import ( "fmt" "log" - "regexp" "strconv" "strings" "time" @@ -17,11 +16,6 @@ import ( "github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags" ) -var guardDutyFilterCriterionValidateFunc = validation.Any( - validation.IsRFC3339Time, - validation.StringMatch(regexp.MustCompile(`^\d+$`), "must be an integer value"), -) - func resourceAwsGuardDutyFilter() *schema.Resource { return &schema.Resource{ Create: resourceAwsGuardDutyFilterCreate, @@ -85,22 +79,22 @@ func resourceAwsGuardDutyFilter() *schema.Resource { "greater_than": { Type: schema.TypeString, Optional: true, - ValidateFunc: guardDutyFilterCriterionValidateFunc, + ValidateFunc: validateTypeStringIsDateOrPositiveInt, }, "greater_than_or_equal": { Type: schema.TypeString, Optional: true, - ValidateFunc: guardDutyFilterCriterionValidateFunc, + ValidateFunc: validateTypeStringIsDateOrPositiveInt, }, "less_than": { Type: schema.TypeString, Optional: true, - ValidateFunc: guardDutyFilterCriterionValidateFunc, + ValidateFunc: validateTypeStringIsDateOrPositiveInt, }, "less_than_or_equal": { Type: schema.TypeString, Optional: true, - ValidateFunc: guardDutyFilterCriterionValidateFunc, + ValidateFunc: validateTypeStringIsDateOrPositiveInt, }, }, }, @@ -374,11 +368,7 @@ func expandConditionIntField(field, v string) (int64, error) { return date.UnixNano() / 1000000, nil } - i, err := strconv.ParseInt(v, 10, 64) - if err != nil { - return 0, err - } - return i, nil + return strconv.ParseInt(v, 10, 64) } func flattenFindingCriteria(findingCriteriaRemote *guardduty.FindingCriteria) []interface{} { diff --git a/aws/validators.go b/aws/validators.go index d5c20782d5d2..df7226f152b2 100644 --- a/aws/validators.go +++ b/aws/validators.go @@ -2500,3 +2500,8 @@ func MapKeysDoNotMatch(r *regexp.Regexp, message string) schema.SchemaValidateFu return warnings, errors } } + +var validateTypeStringIsDateOrPositiveInt = validation.Any( + validation.IsRFC3339Time, + validation.StringMatch(regexp.MustCompile(`^\d+$`), "must be a positive integer value"), +) diff --git a/aws/validators_test.go b/aws/validators_test.go index e9941893c64e..879e23ee9d31 100644 --- a/aws/validators_test.go +++ b/aws/validators_test.go @@ -3308,3 +3308,32 @@ func TestValidateUTCTimestamp(t *testing.T) { } } } + +func TestValidateTypeStringIsDateOrInt(t *testing.T) { + validT := []string{ + "2006-01-02T15:04:05Z", + "2006-01-02T15:04:05-07:00", + "1234", + "0", + } + + for _, f := range validT { + _, errors := validateTypeStringIsDateOrPositiveInt(f, "parameter") + if len(errors) > 0 { + t.Fatalf("expected the value %q to be either RFC 3339 or positive integer, got error %q", f, errors) + } + } + + invalidT := []string{ + "2018-03-01T00:00:00", // No time zone + "ABC", + "-789", + } + + for _, f := range invalidT { + _, errors := validateTypeStringIsDateOrPositiveInt(f, "parameter") + if len(errors) == 0 { + t.Fatalf("expected the value %q to fail validation", f) + } + } +} diff --git a/website/docs/r/acmpca_certificate.html.markdown b/website/docs/r/acmpca_certificate.html.markdown index 1e5d98e102d3..1088ee2135fc 100644 --- a/website/docs/r/acmpca_certificate.html.markdown +++ b/website/docs/r/acmpca_certificate.html.markdown @@ -19,8 +19,10 @@ resource "aws_acmpca_certificate" "example" { certificate_authority_arn = aws_acmpca_certificate_authority.example.arn certificate_signing_request = tls_cert_request.csr.cert_request_pem signing_algorithm = "SHA256WITHRSA" - validity_length = 1 - validity_unit = "YEARS" + validity { + type = "YEARS" + value = 1 + } } resource "aws_acmpca_certificate_authority" "example" { @@ -57,10 +59,14 @@ The following arguments are supported: * `certificate_authority_arn` - (Required) Amazon Resource Name (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` -* `validity_length` - (Required) Used with `validity_unit` as the number to apply with the unit -* `validity_unit` - (Required) The unit of time for certificate validity. Cannot be set to a value higher than the validity of the Certficate Authority. Valid values: `DAYS`, `MONTHS`, `YEARS`, `ABSOLUTE`, `END_DATE`. +* `validity` - (Required) Configures end of the validity period for the certificate. See [validity block](#validity-block) below. * `template_arn` - (Optional) The 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. +### validity block + +* `type` - (Required) Determines how `value` is interpreted. Valid values: `DAYS`, `MONTHS`, `YEARS`, `ABSOLUTE`, `END_DATE`. +* `value` - (Required) If `type` is `DAYS`, `MONTHS`, or `YEARS`, the relative time until the certificate expires. If `type` is `ABSOLUTE`, the date in seconds since the Unix epoch. If `type` is `END_DATE`, the date in RFC 3339 format. + ## Attribute Reference diff --git a/website/docs/r/acmpca_certificate_authority_certificate.html.markdown b/website/docs/r/acmpca_certificate_authority_certificate.html.markdown index b5e7fe5c0022..9cfe798fc48a 100644 --- a/website/docs/r/acmpca_certificate_authority_certificate.html.markdown +++ b/website/docs/r/acmpca_certificate_authority_certificate.html.markdown @@ -29,8 +29,10 @@ resource "aws_acmpca_certificate" "example" { template_arn = "arn:${data.aws_partition.current.partition}:acm-pca:::template/RootCACertificate/V1" - validity_length = 1 - validity_unit = "YEARS" + validity { + type = "YEARS" + value = 1 + } } resource "aws_acmpca_certificate_authority" "example" { @@ -68,8 +70,10 @@ resource "aws_acmpca_certificate" "subordinate" { template_arn = "arn:${data.aws_partition.current.partition}:acm-pca:::template/SubordinateCACertificate_PathLen0/V1" - validity_length = 1 - validity_unit = "YEARS" + validity { + type = "YEARS" + value = 1 + } } resource "aws_acmpca_certificate_authority" "subordinate" { From d802a4320e0ddd5e297d3cf09091e9adf3240db8 Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Fri, 26 Feb 2021 17:36:16 -0800 Subject: [PATCH 29/31] Adds import for certificate authority certificate --- ...rce_aws_acmpca_certificate_authority_certificate.go | 5 +++++ ...ws_acmpca_certificate_authority_certificate_test.go | 10 ++++++++++ 2 files changed, 15 insertions(+) diff --git a/aws/resource_aws_acmpca_certificate_authority_certificate.go b/aws/resource_aws_acmpca_certificate_authority_certificate.go index 30a1784e4fbe..9309f8cf6ece 100644 --- a/aws/resource_aws_acmpca_certificate_authority_certificate.go +++ b/aws/resource_aws_acmpca_certificate_authority_certificate.go @@ -18,6 +18,10 @@ func resourceAwsAcmpcaCertificateAuthorityCertificate() *schema.Resource { Read: resourceAwsAcmpcaCertificateAuthorityCertificateRead, Delete: schema.Noop, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + Schema: map[string]*schema.Schema{ "certificate": { Type: schema.TypeString, @@ -77,6 +81,7 @@ func resourceAwsAcmpcaCertificateAuthorityCertificateRead(d *schema.ResourceData return fmt.Errorf("error reading ACM PCA Certificate Authority Certificate (%s): %w", d.Id(), err) } + d.Set("certificate_authority_arn", d.Id()) d.Set("certificate", aws.StringValue(output.Certificate)) d.Set("certificate_chain", aws.StringValue(output.CertificateChain)) diff --git a/aws/resource_aws_acmpca_certificate_authority_certificate_test.go b/aws/resource_aws_acmpca_certificate_authority_certificate_test.go index b9af499cd56c..bd4231c4506c 100644 --- a/aws/resource_aws_acmpca_certificate_authority_certificate_test.go +++ b/aws/resource_aws_acmpca_certificate_authority_certificate_test.go @@ -31,6 +31,11 @@ func TestAccAwsAcmpcaCertificateAuthorityCertificate_RootCA(t *testing.T) { resource.TestCheckResourceAttrPair(resourceName, "certificate_chain", "aws_acmpca_certificate.test", "certificate_chain"), ), }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, }, }) } @@ -87,6 +92,11 @@ func TestAccAwsAcmpcaCertificateAuthorityCertificate_SubordinateCA(t *testing.T) resource.TestCheckResourceAttrPair(resourceName, "certificate_chain", "aws_acmpca_certificate.test", "certificate_chain"), ), }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, }, }) } From 770578059c1c51e6be12aba79cb9ce2eeda82c21 Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Fri, 26 Feb 2021 17:47:32 -0800 Subject: [PATCH 30/31] Adds CHANGELOG --- .changelog/10213.txt | 7 +++++++ .changelog/17850.txt | 3 +++ 2 files changed, 10 insertions(+) create mode 100644 .changelog/10213.txt create mode 100644 .changelog/17850.txt diff --git a/.changelog/10213.txt b/.changelog/10213.txt new file mode 100644 index 000000000000..350bfe6d3a89 --- /dev/null +++ b/.changelog/10213.txt @@ -0,0 +1,7 @@ +```release-note:new-resource +aws_acmpca_certificate +``` + +```release-note:new-data-source +aws_acmpca_certificate +``` diff --git a/.changelog/17850.txt b/.changelog/17850.txt new file mode 100644 index 000000000000..ccda8dc4aa2f --- /dev/null +++ b/.changelog/17850.txt @@ -0,0 +1,3 @@ +```release-note:new-resource +aws_acmpca_certificate_authority_certificate +``` From 20bb4283ef98335efad541536e3e3eda87345534 Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Wed, 10 Mar 2021 14:32:46 -0800 Subject: [PATCH 31/31] Returns general errors from finder --- aws/internal/service/acmpca/finder/finder.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/aws/internal/service/acmpca/finder/finder.go b/aws/internal/service/acmpca/finder/finder.go index 2d4934f4b972..7cf9c2f36a86 100644 --- a/aws/internal/service/acmpca/finder/finder.go +++ b/aws/internal/service/acmpca/finder/finder.go @@ -40,6 +40,9 @@ func CertificateAuthorityCertificateByARN(conn *acmpca.ACMPCA, arn string) (*acm LastRequest: input, } } + if err != nil { + return nil, err + } if output == nil { return nil, &resource.NotFoundError{