Skip to content

Commit

Permalink
Merge pull request #17850 from hashicorp/acmpca-private-cert
Browse files Browse the repository at this point in the history
ACM PCA: add resource to issue certificates from private CA and import CA certificates
  • Loading branch information
gdavison authored Mar 11, 2021
2 parents 40f8371 + 20bb428 commit a374549
Show file tree
Hide file tree
Showing 24 changed files with 1,797 additions and 68 deletions.
7 changes: 7 additions & 0 deletions .changelog/10213.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
```release-note:new-resource
aws_acmpca_certificate
```

```release-note:new-data-source
aws_acmpca_certificate
```
3 changes: 3 additions & 0 deletions .changelog/17850.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:new-resource
aws_acmpca_certificate_authority_certificate
```
60 changes: 60 additions & 0 deletions aws/data_source_aws_acmpca_certificate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
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"
)

func dataSourceAwsAcmpcaCertificate() *schema.Resource {
return &schema.Resource{
Read: dataSourceAwsAcmpcaCertificateRead,

Schema: map[string]*schema.Schema{
"arn": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validateArn,
},
"certificate_authority_arn": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validateArn,
},
"certificate": {
Type: schema.TypeString,
Computed: true,
},
"certificate_chain": {
Type: schema.TypeString,
Computed: true,
},
},
}
}

func dataSourceAwsAcmpcaCertificateRead(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 ACM PCA Certificate: %s", getCertificateInput)

certificateOutput, err := conn.GetCertificate(getCertificateInput)
if err != nil {
return fmt.Errorf("error reading ACM PCA Certificate (%s): %w", certificateArn, err)
}

d.SetId(certificateArn)
d.Set("certificate", aws.StringValue(certificateOutput.Certificate))
d.Set("certificate_chain", aws.StringValue(certificateOutput.CertificateChain))

return nil
}
16 changes: 8 additions & 8 deletions aws/data_source_aws_acmpca_certificate_authority.go
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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)
}
}

Expand All @@ -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", "")
Expand All @@ -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 {
Expand Down
87 changes: 87 additions & 0 deletions aws/data_source_aws_acmpca_certificate_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package aws

import (
"fmt"
"regexp"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
)

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: testAccDataSourceAwsAcmpcaCertificateConfig_NonExistent,
ExpectError: regexp.MustCompile(`ResourceNotFoundException`),
},
{
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"),
),
},
},
})
}

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 {
type = "YEARS"
value = 1
}
}
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)
}

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"
}
data "aws_caller_identity" "current" {}
data "aws_partition" "current" {}
data "aws_region" "current" {}
`
30 changes: 30 additions & 0 deletions aws/internal/service/acmpca/finder/finder.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -23,3 +25,31 @@ 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 err != nil {
return nil, err
}

if output == nil {
return nil, &resource.NotFoundError{
Message: "empty result",
LastRequest: input,
}
}

return output, nil
}
4 changes: 4 additions & 0 deletions aws/internal/service/acmpca/waiter/waiter.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,7 @@ func CertificateAuthorityCreated(conn *acmpca.ACMPCA, arn string, timeout time.D

return nil, err
}

const (
CertificateAuthorityActiveTimeout = 1 * time.Minute
)
3 changes: 3 additions & 0 deletions aws/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ func Provider() *schema.Provider {
DataSourcesMap: map[string]*schema.Resource{
"aws_acm_certificate": dataSourceAwsAcmCertificate(),
"aws_acmpca_certificate_authority": dataSourceAwsAcmpcaCertificateAuthority(),
"aws_acmpca_certificate": dataSourceAwsAcmpcaCertificate(),
"aws_ami": dataSourceAwsAmi(),
"aws_ami_ids": dataSourceAwsAmiIds(),
"aws_api_gateway_api_key": dataSourceAwsApiGatewayApiKey(),
Expand Down Expand Up @@ -412,6 +413,8 @@ 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_certificate": resourceAwsAcmpcaCertificate(),
"aws_ami": resourceAwsAmi(),
"aws_ami_copy": resourceAwsAmiCopy(),
"aws_ami_from_instance": resourceAwsAmiFromInstance(),
Expand Down
Loading

0 comments on commit a374549

Please sign in to comment.