Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Add usage_mode to support short-lived certificates #27496

Merged
merged 19 commits into from
Oct 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
1f3ccfc
Add `usage_mode` to support short lived private certificates
bschaatsbergen Oct 26, 2022
e4cc74d
Create 27496.txt
bschaatsbergen Oct 26, 2022
6096e99
apply suggested fix from semgrep
bschaatsbergen Oct 26, 2022
cfa416f
add Basic short-lived certificate example to docs
bschaatsbergen Oct 26, 2022
c1a0953
tabs to spaces
bschaatsbergen Oct 26, 2022
9a873ff
Add `usage_mode` attribute documentation
bschaatsbergen Oct 26, 2022
dcecb1b
add `usage_mode` to data source
bschaatsbergen Oct 26, 2022
32d7520
Update certificate authority data source tests to include `usage_mode`
bschaatsbergen Oct 26, 2022
581c1e7
Add `usage_mode` to acmpca_certificate_authority data source docs
bschaatsbergen Oct 26, 2022
91e5f25
Update 27496.txt
bschaatsbergen Oct 26, 2022
c2d6883
Tweak CHANGELOG entry.
ewbankkit Oct 27, 2022
0601c6c
r/aws_acmpca_certificate_authority: Alphabetize attributes.
ewbankkit Oct 27, 2022
e890ab9
r/aws_acmpca_certificate_authority: Use '_Values()' function (#14601).
ewbankkit Oct 27, 2022
adc6a93
r/aws_acmpca_certificate_authority: Correct some error messages.
ewbankkit Oct 27, 2022
73909c3
d/aws_acmpca_certificate_authority: Alphabetize attributes.
ewbankkit Oct 27, 2022
0dba5e5
d/aws_acmpca_certificate_authority: Correct some error messages.
ewbankkit Oct 27, 2022
5f4de21
d/aws_acmpca_certificate_authority: Use 'ComposeAggregateTestCheckFun…
ewbankkit Oct 27, 2022
09b0318
r/aws_acmpca_certificate_authority: Tidy up acceptance tests.
ewbankkit Oct 27, 2022
6412e24
Fix typo.
ewbankkit Oct 27, 2022
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/27496.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
```release-note:enhancement
resource/aws_acmpca_certificate_authority: Add `usage_mode` argument to support [short-lived certificates](https://docs.aws.amazon.com/privateca/latest/userguide/short-lived-certificates.html)
```

```release-note:enhancement
data-source/aws_acmpca_certificate_authority: Add `usage_mode` attribute
```
81 changes: 38 additions & 43 deletions internal/service/acmpca/certificate_authority.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ func ResourceCertificateAuthority() *schema.Resource {
Read: resourceCertificateAuthorityRead,
Update: resourceCertificateAuthorityUpdate,
Delete: resourceCertificateAuthorityDelete,

Importer: &schema.ResourceImporter{
State: func(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
d.Set(
Expand All @@ -40,9 +41,11 @@ func ResourceCertificateAuthority() *schema.Resource {
return []*schema.ResourceData{d}, nil
},
},

Timeouts: &schema.ResourceTimeout{
Create: schema.DefaultTimeout(1 * time.Minute),
},

MigrateState: resourceCertificateAuthorityMigrateState,
SchemaVersion: 1,

Expand All @@ -63,28 +66,16 @@ func ResourceCertificateAuthority() *schema.Resource {
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"key_algorithm": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validation.StringInSlice([]string{
acmpca.KeyAlgorithmEcPrime256v1,
acmpca.KeyAlgorithmEcSecp384r1,
acmpca.KeyAlgorithmRsa2048,
acmpca.KeyAlgorithmRsa4096,
}, false),
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validation.StringInSlice(acmpca.KeyAlgorithm_Values(), false),
},
"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),
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validation.StringInSlice(acmpca.SigningAlgorithm_Values(), false),
},
// https://docs.aws.amazon.com/privateca/latest/APIReference/API_ASN1Subject.html
"subject": {
Expand Down Expand Up @@ -199,6 +190,15 @@ func ResourceCertificateAuthority() *schema.Resource {
Type: schema.TypeString,
Computed: true,
},
"permanent_deletion_time_in_days": {
Type: schema.TypeInt,
Optional: true,
Default: certificateAuthorityPermanentDeletionTimeInDaysDefault,
ValidateFunc: validation.IntBetween(
certificateAuthorityPermanentDeletionTimeInDaysMin,
certificateAuthorityPermanentDeletionTimeInDaysMax,
),
},
// https://docs.aws.amazon.com/privateca/latest/APIReference/API_RevocationConfiguration.html
"revocation_configuration": {
Type: schema.TypeList,
Expand Down Expand Up @@ -293,26 +293,20 @@ func ResourceCertificateAuthority() *schema.Resource {
Computed: true,
Deprecated: "The reported value of the \"status\" attribute is often inaccurate. Use the resource's \"enabled\" attribute to explicitly set status.",
},
"permanent_deletion_time_in_days": {
Type: schema.TypeInt,
Optional: true,
Default: certificateAuthorityPermanentDeletionTimeInDaysDefault,
ValidateFunc: validation.IntBetween(
certificateAuthorityPermanentDeletionTimeInDaysMin,
certificateAuthorityPermanentDeletionTimeInDaysMax,
),
},
"tags": tftags.TagsSchema(),
"tags_all": tftags.TagsSchemaComputed(),
"type": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Default: acmpca.CertificateAuthorityTypeSubordinate,
ValidateFunc: validation.StringInSlice([]string{
acmpca.CertificateAuthorityTypeRoot,
acmpca.CertificateAuthorityTypeSubordinate,
}, false),
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Default: acmpca.CertificateAuthorityTypeSubordinate,
ValidateFunc: validation.StringInSlice(acmpca.CertificateAuthorityType_Values(), false),
},
"usage_mode": {
Type: schema.TypeString,
Computed: true,
Optional: true,
ValidateFunc: validation.StringInSlice(acmpca.CertificateAuthorityUsageMode_Values(), false),
},
},

Expand All @@ -332,6 +326,10 @@ func resourceCertificateAuthorityCreate(d *schema.ResourceData, meta interface{}
RevocationConfiguration: expandRevocationConfiguration(d.Get("revocation_configuration").([]interface{})),
}

if v, ok := d.GetOk("usage_mode"); ok {
input.UsageMode = aws.String(v.(string))
}

if len(tags) > 0 {
input.Tags = Tags(tags.IgnoreAWS())
}
Expand Down Expand Up @@ -396,22 +394,19 @@ func resourceCertificateAuthorityRead(d *schema.ResourceData, meta interface{})
}

d.Set("arn", certificateAuthority.Arn)

if err := d.Set("certificate_authority_configuration", flattenCertificateAuthorityConfiguration(certificateAuthority.CertificateAuthorityConfiguration)); err != nil {
return fmt.Errorf("setting tags: %s", err)
return fmt.Errorf("setting certificate_authority_configuration: %w", err)
}

d.Set("enabled", (aws.StringValue(certificateAuthority.Status) != acmpca.CertificateAuthorityStatusDisabled))
d.Set("not_after", aws.TimeValue(certificateAuthority.NotAfter).Format(time.RFC3339))
d.Set("not_before", aws.TimeValue(certificateAuthority.NotBefore).Format(time.RFC3339))

if err := d.Set("revocation_configuration", flattenRevocationConfiguration(certificateAuthority.RevocationConfiguration)); err != nil {
return fmt.Errorf("setting tags: %s", err)
return fmt.Errorf("setting revocation_configuration: %w", err)
}

d.Set("serial", certificateAuthority.Serial)
d.Set("status", certificateAuthority.Status)
d.Set("type", certificateAuthority.Type)
d.Set("usage_mode", certificateAuthority.UsageMode)

getCertificateAuthorityCertificateInput := &acmpca.GetCertificateAuthorityCertificateInput{
CertificateAuthorityArn: aws.String(d.Id()),
Expand Down
21 changes: 12 additions & 9 deletions internal/service/acmpca/certificate_authority_data_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,10 @@ func DataSourceCertificateAuthority() *schema.Resource {
Type: schema.TypeString,
Computed: true,
},
"usage_mode": {
Type: schema.TypeString,
Computed: true,
},
},
}
}
Expand All @@ -130,25 +134,24 @@ func dataSourceCertificateAuthorityRead(d *schema.ResourceData, meta interface{}

describeCertificateAuthorityOutput, err := conn.DescribeCertificateAuthority(describeCertificateAuthorityInput)
if err != nil {
return fmt.Errorf("error reading ACM PCA Certificate Authority: %w", err)
return fmt.Errorf("reading ACM PCA Certificate Authority (%s): %w", certificateAuthorityARN, err)
}

if describeCertificateAuthorityOutput.CertificateAuthority == nil {
return fmt.Errorf("error reading ACM PCA Certificate Authority: not found")
return fmt.Errorf("reading ACM PCA Certificate Authority: not found")
}
certificateAuthority := describeCertificateAuthorityOutput.CertificateAuthority

d.Set("arn", certificateAuthority.Arn)
d.Set("not_after", aws.TimeValue(certificateAuthority.NotAfter).Format(time.RFC3339))
d.Set("not_before", aws.TimeValue(certificateAuthority.NotBefore).Format(time.RFC3339))

if err := d.Set("revocation_configuration", flattenRevocationConfiguration(certificateAuthority.RevocationConfiguration)); err != nil {
return fmt.Errorf("error setting tags: %w", err)
return fmt.Errorf("setting revocation_configuration: %w", err)
}

d.Set("serial", certificateAuthority.Serial)
d.Set("status", certificateAuthority.Status)
d.Set("type", certificateAuthority.Type)
d.Set("usage_mode", certificateAuthority.UsageMode)

getCertificateAuthorityCertificateInput := &acmpca.GetCertificateAuthorityCertificateInput{
CertificateAuthorityArn: aws.String(certificateAuthorityARN),
Expand All @@ -161,7 +164,7 @@ func dataSourceCertificateAuthorityRead(d *schema.ResourceData, meta interface{}
// 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 ACM PCA Certificate Authority Certificate: %w", err)
return fmt.Errorf("reading ACM PCA Certificate Authority Certificate: %w", err)
}
}

Expand All @@ -180,7 +183,7 @@ func dataSourceCertificateAuthorityRead(d *schema.ResourceData, meta interface{}

getCertificateAuthorityCsrOutput, err := conn.GetCertificateAuthorityCsr(getCertificateAuthorityCsrInput)
if err != nil {
return fmt.Errorf("error reading ACM PCA Certificate Authority Certificate Signing Request: %w", err)
return fmt.Errorf("reading ACM PCA Certificate Authority Certificate Signing Request: %w", err)
}

d.Set("certificate_signing_request", "")
Expand All @@ -191,11 +194,11 @@ func dataSourceCertificateAuthorityRead(d *schema.ResourceData, meta interface{}
tags, err := ListTags(conn, certificateAuthorityARN)

if err != nil {
return fmt.Errorf("error listing tags for ACM PCA Certificate Authority (%s): %w", certificateAuthorityARN, err)
return fmt.Errorf("listing tags for ACM PCA Certificate Authority (%s): %w", certificateAuthorityARN, err)
}

if err := d.Set("tags", tags.IgnoreAWS().IgnoreConfig(ignoreTagsConfig).Map()); err != nil {
return fmt.Errorf("error setting tags: %w", err)
return fmt.Errorf("setting tags: %w", err)
}

d.SetId(certificateAuthorityARN)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func TestAccACMPCACertificateAuthorityDataSource_basic(t *testing.T) {
},
{
Config: testAccCertificateAuthorityDataSourceConfig_arn(commonName),
Check: resource.ComposeTestCheckFunc(
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttrPair(datasourceName, "arn", resourceName, "arn"),
resource.TestCheckResourceAttrPair(datasourceName, "certificate", resourceName, "certificate"),
resource.TestCheckResourceAttrPair(datasourceName, "certificate_chain", resourceName, "certificate_chain"),
Expand All @@ -41,6 +41,7 @@ func TestAccACMPCACertificateAuthorityDataSource_basic(t *testing.T) {
resource.TestCheckResourceAttrPair(datasourceName, "status", resourceName, "status"),
resource.TestCheckResourceAttrPair(datasourceName, "tags.%", resourceName, "tags.%"),
resource.TestCheckResourceAttrPair(datasourceName, "type", resourceName, "type"),
resource.TestCheckResourceAttrPair(datasourceName, "usage_mode", resourceName, "usage_mode"),
),
},
},
Expand All @@ -64,7 +65,7 @@ func TestAccACMPCACertificateAuthorityDataSource_s3ObjectACL(t *testing.T) {
},
{
Config: testAccCertificateAuthorityDataSourceConfig_s3ObjectACLARN(commonName),
Check: resource.ComposeTestCheckFunc(
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttrPair(datasourceName, "arn", resourceName, "arn"),
resource.TestCheckResourceAttrPair(datasourceName, "certificate", resourceName, "certificate"),
resource.TestCheckResourceAttrPair(datasourceName, "certificate_chain", resourceName, "certificate_chain"),
Expand All @@ -82,6 +83,7 @@ func TestAccACMPCACertificateAuthorityDataSource_s3ObjectACL(t *testing.T) {
resource.TestCheckResourceAttrPair(datasourceName, "status", resourceName, "status"),
resource.TestCheckResourceAttrPair(datasourceName, "tags.%", resourceName, "tags.%"),
resource.TestCheckResourceAttrPair(datasourceName, "type", resourceName, "type"),
resource.TestCheckResourceAttrPair(datasourceName, "usage_mode", resourceName, "usage_mode"),
),
},
},
Expand Down
53 changes: 53 additions & 0 deletions internal/service/acmpca/certificate_authority_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ func TestAccACMPCACertificateAuthority_basic(t *testing.T) {
resource.TestCheckResourceAttr(resourceName, "status", "PENDING_CERTIFICATE"),
resource.TestCheckResourceAttr(resourceName, "tags.%", "0"),
resource.TestCheckResourceAttr(resourceName, "type", "SUBORDINATE"),
resource.TestCheckResourceAttr(resourceName, "usage_mode", "GENERAL_PURPOSE"),
),
},
{
Expand Down Expand Up @@ -138,6 +139,37 @@ func TestAccACMPCACertificateAuthority_enabledDeprecated(t *testing.T) {
})
}

func TestAccACMPCACertificateAuthority_usageMode(t *testing.T) {
var certificateAuthority acmpca.CertificateAuthority
resourceName := "aws_acmpca_certificate_authority.test"

commonName := acctest.RandomDomainName()

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(t) },
ErrorCheck: acctest.ErrorCheck(t, acmpca.EndpointsID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
CheckDestroy: testAccCheckCertificateAuthorityDestroy,
Steps: []resource.TestStep{
{
Config: testAccCertificateAuthorityConfig_usageMode(commonName, acmpca.CertificateAuthorityTypeRoot, "SHORT_LIVED_CERTIFICATE"),
Check: resource.ComposeTestCheckFunc(
acctest.CheckACMPCACertificateAuthorityExists(resourceName, &certificateAuthority),
resource.TestCheckResourceAttr(resourceName, "usage_mode", "SHORT_LIVED_CERTIFICATE"),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{
"permanent_deletion_time_in_days",
},
},
},
})
}

func TestAccACMPCACertificateAuthority_deleteFromActiveState(t *testing.T) {
var certificateAuthority acmpca.CertificateAuthority
resourceName := "aws_acmpca_certificate_authority.test"
Expand Down Expand Up @@ -201,6 +233,7 @@ func TestAccACMPCACertificateAuthority_RevocationConfiguration_empty(t *testing.
resource.TestCheckResourceAttr(resourceName, "status", "PENDING_CERTIFICATE"),
resource.TestCheckResourceAttr(resourceName, "tags.%", "0"),
resource.TestCheckResourceAttr(resourceName, "type", "SUBORDINATE"),
resource.TestCheckResourceAttr(resourceName, "usage_mode", "GENERAL_PURPOSE"),
),
},
{
Expand Down Expand Up @@ -743,6 +776,26 @@ resource "aws_acmpca_certificate_authority" "test" {
`, enabled, certificateAuthorityType, commonName)
}

func testAccCertificateAuthorityConfig_usageMode(commonName, certificateAuthorityType string, usageMode string) string {
return fmt.Sprintf(`
resource "aws_acmpca_certificate_authority" "test" {
enabled = true
usage_mode = %[1]q
permanent_deletion_time_in_days = 7
type = %[2]q

certificate_authority_configuration {
key_algorithm = "RSA_4096"
signing_algorithm = "SHA512WITHRSA"

subject {
common_name = %[3]q
}
}
}
`, usageMode, certificateAuthorityType, commonName)
}

func testAccCertificateAuthorityConfig_root(commonName string) string {
return fmt.Sprintf(`
resource "aws_acmpca_certificate_authority" "test" {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ In addition to all arguments above, the following attributes are exported:
* `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.
* `certificate_signing_request` - The base64 PEM-encoded certificate signing request (CSR) for your private CA certificate.
* `usage_mode` - Specifies whether the CA issues general-purpose certificates that typically require a revocation mechanism, or short-lived certificates that may optionally omit revocation because they expire quickly.
* `not_after` - Date and time after which the certificate authority is not valid. Only available after the certificate authority certificate has been imported.
* `not_before` - Date and time before which the certificate authority is not valid. Only available after the certificate authority certificate has been imported.
* `revocation_configuration` - Nested attribute containing revocation configuration.
Expand Down
17 changes: 17 additions & 0 deletions website/docs/r/acmpca_certificate_authority.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,22 @@ resource "aws_acmpca_certificate_authority" "example" {
}
```

### Short-lived certificate

```terraform
resource "aws_acmpca_certificate_authority" "example" {
usage_mode = "SHORT_LIVED_CERTIFICATE"
certificate_authority_configuration {
key_algorithm = "RSA_4096"
signing_algorithm = "SHA512WITHRSA"

subject {
common_name = "example.com"
}
}
}
```

### Enable Certificate Revocation List

```terraform
Expand Down Expand Up @@ -94,6 +110,7 @@ The following arguments are supported:
* `certificate_authority_configuration` - (Required) Nested argument containing algorithms and certificate subject information. Defined below.
* `enabled` - (Optional) Whether the certificate authority is enabled or disabled. Defaults to `true`.
* `revocation_configuration` - (Optional) Nested argument containing revocation configuration. Defined below.
* `usage_mode` - (Optional) Specifies whether the CA issues general-purpose certificates that typically require a revocation mechanism, or short-lived certificates that may optionally omit revocation because they expire quickly. Short-lived certificate validity is limited to seven days. Defaults to `GENERAL_PURPOSE`. Valid values: `GENERAL_PURPOSE` and `SHORT_LIVED_CERTIFICATE`.
* `tags` - (Optional) Key-value map of user-defined tags that are attached to the certificate authority. If configured with a provider [`default_tags` configuration block](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#default_tags-configuration-block) present, tags with matching keys will overwrite those defined at the provider-level.
* `type` - (Optional) Type of the certificate authority. Defaults to `SUBORDINATE`. Valid values: `ROOT` and `SUBORDINATE`.
* `permanent_deletion_time_in_days` - (Optional) Number of days to make a CA restorable after it has been deleted, must be between 7 to 30 days, with default to 30 days.
Expand Down