Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

ACM PCA: add resource to issue certificates from private CA and import CA certificates #17850

Merged
merged 44 commits into from
Mar 11, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
6807a0b
private cert issuing
dvdliao Sep 23, 2019
b88a02c
tests
dvdliao Sep 24, 2019
557335d
data resources and docs
dvdliao Sep 24, 2019
f5f1fad
minor
dvdliao Sep 24, 2019
853bdf5
Merge branch 'master' into acmpca-private-cert
dvdliao Sep 24, 2019
142d8be
typos
dvdliao Sep 24, 2019
84b9f92
Merge branch 'master' into acmpca-private-cert
dvdliao Sep 24, 2019
c8fad70
Merge branch 'master' into acmpca-private-cert
dvdliao Sep 26, 2019
74f9787
update changelog
dvdliao Sep 26, 2019
de44fd3
Merge branch 'master' into acmpca-private-cert
dvdliao Sep 27, 2019
6b4fdc1
Merge branch 'master' into acmpca-private-cert
dvdliao Oct 1, 2019
5252c6e
Merge branch 'master' into acmpca-private-cert
dvdliao Oct 4, 2019
eee3a97
fix conflicts
dvdliao Oct 4, 2019
89170da
Merge branch 'master' into acmpca-private-cert
dvdliao Oct 4, 2019
ff75379
Merge branch 'master' into acmpca-private-cert
dvdliao Nov 8, 2019
f682fb0
update changelog
dvdliao Nov 8, 2019
90cf76c
Merge remote-tracking branch 'upstream/master' into acmpca-private-cert
dvdliao May 3, 2020
b4d2f41
pulling in master
dvdliao May 3, 2020
4730486
no changelog
dvdliao May 3, 2020
1700783
fix imports, docs subcat
dvdliao May 3, 2020
9a3be90
update tests to not use tls provider
dvdliao May 3, 2020
d7f8d67
lints
dvdliao May 3, 2020
2857199
fixing tests and add comment
dvdliao May 3, 2020
4b368a7
revert endline
dvdliao May 5, 2020
58142bd
cr
dvdliao May 5, 2020
f768237
Merge branch 'master' into acmpca-private-cert
dvdliao May 5, 2020
5b36e37
lintignore
dvdliao May 5, 2020
1c65502
Update resource_aws_acmpca_private_certificate.go
dvdliao May 5, 2020
b8efe13
Merge branch 'master' into acmpca-private-cert
dvdliao Jun 2, 2020
d4505bc
cr
dvdliao Jun 2, 2020
1e7d722
Merge branch 'main' into acmpca-private-cert
gdavison Feb 22, 2021
2096bf0
Makes ARNs partition-independent
gdavison Feb 23, 2021
5ed422b
Supports creating root certificates for private CA
gdavison Feb 24, 2021
927c0bf
Updates output to "ACM PCA" from "ACMPCA" to match AWS naming
gdavison Feb 24, 2021
0bfdbe7
Adds ability to import certificate into certificate authority
gdavison Feb 25, 2021
a97e6af
Renamed resource and data source from acmpca_private_certificate to a…
gdavison Feb 25, 2021
35ae85b
Add back test for end entity certificate and test updating root CA
gdavison Feb 25, 2021
07fe7c6
Adds test for issuing subordinate CA certificate
gdavison Feb 25, 2021
78ec08e
Adds import to certificate
gdavison Feb 25, 2021
5f3436c
Refactors certificate validity parameters to match API
gdavison Feb 27, 2021
d802a43
Adds import for certificate authority certificate
gdavison Feb 27, 2021
7705780
Adds CHANGELOG
gdavison Feb 27, 2021
28acee6
Merge branch 'main' into acmpca-private-cert
gdavison Feb 27, 2021
20bb428
Returns general errors from finder
gdavison Mar 10, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 {
gdavison marked this conversation as resolved.
Show resolved Hide resolved
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