Skip to content

Commit a48f1ac

Browse files
authored
Merge pull request #37140 from aristosvo/key/automatic-rotation
`r/kms_key`: add support for `rotation_period_in_days`
2 parents 3a7abcd + 83dec92 commit a48f1ac

File tree

4 files changed

+104
-22
lines changed

4 files changed

+104
-22
lines changed

.changelog/37140.txt

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```release-note:enhancement
2+
resource/aws_kms_key: Add `rotation_period_in_days` argument
3+
```

internal/service/kms/key.go

+40-21
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,13 @@ func resourceKey() *schema.Resource {
122122
return json
123123
},
124124
},
125+
"rotation_period_in_days": {
126+
Type: schema.TypeInt,
127+
Optional: true,
128+
Computed: true,
129+
ValidateFunc: validation.IntBetween(90, 2560),
130+
RequiredWith: []string{"enable_key_rotation"},
131+
},
125132
names.AttrTags: tftags.TagsSchema(),
126133
names.AttrTagsAll: tftags.TagsSchemaComputed(),
127134
"xks_key_id": {
@@ -189,8 +196,8 @@ func resourceKeyCreate(ctx context.Context, d *schema.ResourceData, meta interfa
189196

190197
ctx = tflog.SetField(ctx, logging.KeyResourceId, d.Id())
191198

192-
if enableKeyRotation := d.Get("enable_key_rotation").(bool); enableKeyRotation {
193-
if err := updateKeyRotationEnabled(ctx, conn, "KMS Key", d.Id(), enableKeyRotation); err != nil {
199+
if enableKeyRotation, rotationPeriod := d.Get("enable_key_rotation").(bool), d.Get("rotation_period_in_days").(int); enableKeyRotation {
200+
if err := updateKeyRotationEnabled(ctx, conn, "KMS Key", d.Id(), enableKeyRotation, rotationPeriod); err != nil {
194201
return sdkdiag.AppendFromErr(diags, err)
195202
}
196203
}
@@ -248,6 +255,7 @@ func resourceKeyRead(ctx context.Context, d *schema.ResourceData, meta interface
248255
d.Set("key_id", key.metadata.KeyId)
249256
d.Set("key_usage", key.metadata.KeyUsage)
250257
d.Set("multi_region", key.metadata.MultiRegion)
258+
d.Set("rotation_period_in_days", key.rotationPeriodInDays)
251259
if key.metadata.XksKeyConfiguration != nil {
252260
d.Set("xks_key_id", key.metadata.XksKeyConfiguration.Id)
253261
} else {
@@ -279,8 +287,10 @@ func resourceKeyUpdate(ctx context.Context, d *schema.ResourceData, meta interfa
279287
}
280288
}
281289

282-
if hasChange, enable := d.HasChange("enable_key_rotation"), d.Get("enable_key_rotation").(bool); hasChange {
283-
if err := updateKeyRotationEnabled(ctx, conn, "KMS Key", d.Id(), enable); err != nil {
290+
if hasChange, hasChangedRotationPeriod,
291+
enable, rotationPeriod := d.HasChange("enable_key_rotation"), d.HasChange("rotation_period_in_days"),
292+
d.Get("enable_key_rotation").(bool), d.Get("rotation_period_in_days").(int); hasChange || (enable && hasChangedRotationPeriod) {
293+
if err := updateKeyRotationEnabled(ctx, conn, "KMS Key", d.Id(), enable, rotationPeriod); err != nil {
284294
return sdkdiag.AppendFromErr(diags, err)
285295
}
286296
}
@@ -344,10 +354,11 @@ func resourceKeyDelete(ctx context.Context, d *schema.ResourceData, meta interfa
344354
}
345355

346356
type kmsKeyInfo struct {
347-
metadata *awstypes.KeyMetadata
348-
policy string
349-
rotation *bool
350-
tags []awstypes.Tag
357+
metadata *awstypes.KeyMetadata
358+
policy string
359+
rotation *bool
360+
rotationPeriodInDays *int32
361+
tags []awstypes.Tag
351362
}
352363

353364
func findKeyInfo(ctx context.Context, conn *kms.Client, keyID string, isNewResource bool) (*kmsKeyInfo, error) {
@@ -375,7 +386,7 @@ func findKeyInfo(ctx context.Context, conn *kms.Client, keyID string, isNewResou
375386
}
376387

377388
if key.metadata.Origin == awstypes.OriginTypeAwsKms {
378-
key.rotation, err = findKeyRotationEnabledByKeyID(ctx, conn, keyID)
389+
key.rotation, key.rotationPeriodInDays, err = findKeyRotationEnabledByKeyID(ctx, conn, keyID)
379390

380391
if err != nil {
381392
return nil, fmt.Errorf("reading KMS Key (%s) rotation enabled: %w", keyID, err)
@@ -486,29 +497,29 @@ func findKeyPolicyByTwoPartKey(ctx context.Context, conn *kms.Client, keyID, pol
486497
return output.Policy, nil
487498
}
488499

489-
func findKeyRotationEnabledByKeyID(ctx context.Context, conn *kms.Client, keyID string) (*bool, error) {
500+
func findKeyRotationEnabledByKeyID(ctx context.Context, conn *kms.Client, keyID string) (*bool, *int32, error) {
490501
input := &kms.GetKeyRotationStatusInput{
491502
KeyId: aws.String(keyID),
492503
}
493504

494505
output, err := conn.GetKeyRotationStatus(ctx, input)
495506

496507
if errs.IsA[*awstypes.NotFoundException](err) {
497-
return nil, &retry.NotFoundError{
508+
return nil, nil, &retry.NotFoundError{
498509
LastError: err,
499510
LastRequest: input,
500511
}
501512
}
502513

503514
if err != nil {
504-
return nil, err
515+
return nil, nil, err
505516
}
506517

507518
if output == nil {
508-
return nil, tfresource.NewEmptyResultError(input)
519+
return nil, nil, tfresource.NewEmptyResultError(input)
509520
}
510521

511-
return aws.Bool(output.KeyRotationEnabled), nil
522+
return aws.Bool(output.KeyRotationEnabled), output.RotationPeriodInDays, nil
512523
}
513524

514525
func updateKeyDescription(ctx context.Context, conn *kms.Client, resourceTypeName, keyID, description string) error {
@@ -597,17 +608,21 @@ func updateKeyPolicy(ctx context.Context, conn *kms.Client, resourceTypeName, ke
597608
return nil
598609
}
599610

600-
func updateKeyRotationEnabled(ctx context.Context, conn *kms.Client, resourceTypeName, keyID string, enabled bool) error {
611+
func updateKeyRotationEnabled(ctx context.Context, conn *kms.Client, resourceTypeName, keyID string, enabled bool, rotationPeriod int) error {
601612
var action string
602613

603614
updateFunc := func() (interface{}, error) {
604615
var err error
605616

606617
if enabled {
607618
action = "enabling"
608-
_, err = conn.EnableKeyRotation(ctx, &kms.EnableKeyRotationInput{
619+
input := kms.EnableKeyRotationInput{
609620
KeyId: aws.String(keyID),
610-
})
621+
}
622+
if rotationPeriod > 0 {
623+
input.RotationPeriodInDays = aws.Int32(int32(rotationPeriod))
624+
}
625+
_, err = conn.EnableKeyRotation(ctx, &input)
611626
} else {
612627
action = "disabling"
613628
_, err = conn.DisableKeyRotation(ctx, &kms.DisableKeyRotationInput{
@@ -623,7 +638,7 @@ func updateKeyRotationEnabled(ctx context.Context, conn *kms.Client, resourceTyp
623638
}
624639

625640
// Wait for propagation since KMS is eventually consistent.
626-
if err := waitKeyRotationEnabledPropagated(ctx, conn, keyID, enabled); err != nil {
641+
if err := waitKeyRotationEnabledPropagated(ctx, conn, keyID, enabled, rotationPeriod); err != nil {
627642
return fmt.Errorf("waiting for %s (%s) rotation update: %w", resourceTypeName, keyID, err)
628643
}
629644

@@ -722,9 +737,9 @@ func waitKeyPolicyPropagated(ctx context.Context, conn *kms.Client, keyID, polic
722737
return tfresource.WaitUntil(ctx, timeout, checkFunc, opts)
723738
}
724739

725-
func waitKeyRotationEnabledPropagated(ctx context.Context, conn *kms.Client, keyID string, enabled bool) error {
740+
func waitKeyRotationEnabledPropagated(ctx context.Context, conn *kms.Client, keyID string, enabled bool, rotationPeriodWant int) error {
726741
checkFunc := func() (bool, error) {
727-
output, err := findKeyRotationEnabledByKeyID(ctx, conn, keyID)
742+
rotation, rotationPeriodGot, err := findKeyRotationEnabledByKeyID(ctx, conn, keyID)
728743

729744
if tfresource.NotFound(err) {
730745
return false, nil
@@ -734,7 +749,11 @@ func waitKeyRotationEnabledPropagated(ctx context.Context, conn *kms.Client, key
734749
return false, err
735750
}
736751

737-
return aws.ToBool(output) == enabled, nil
752+
if rotationPeriodWant != 0 && rotationPeriodGot != nil {
753+
return aws.ToBool(rotation) == enabled && aws.ToInt32(rotationPeriodGot) == int32(rotationPeriodWant), nil
754+
}
755+
756+
return aws.ToBool(rotation) == enabled, nil
738757
}
739758
opts := tfresource.WaitOpts{
740759
ContinuousTargetOccurence: 5,

internal/service/kms/key_test.go

+59
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,54 @@ func TestAccKMSKey_isEnabled(t *testing.T) {
458458
})
459459
}
460460

461+
func TestAccKMSKey_rotation(t *testing.T) {
462+
ctx := acctest.Context(t)
463+
var key1, key2, key3 awstypes.KeyMetadata
464+
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
465+
resourceName := "aws_kms_key.test"
466+
467+
resource.ParallelTest(t, resource.TestCase{
468+
PreCheck: func() { acctest.PreCheck(ctx, t) },
469+
ErrorCheck: acctest.ErrorCheck(t, names.KMSServiceID),
470+
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
471+
CheckDestroy: testAccCheckKeyDestroy(ctx),
472+
Steps: []resource.TestStep{
473+
{
474+
Config: testAccKeyConfig_enabledRotation(rName),
475+
Check: resource.ComposeTestCheckFunc(
476+
testAccCheckKeyExists(ctx, resourceName, &key1),
477+
resource.TestCheckResourceAttr(resourceName, "is_enabled", "true"),
478+
resource.TestCheckResourceAttr(resourceName, "enable_key_rotation", "true"),
479+
),
480+
},
481+
{
482+
ResourceName: resourceName,
483+
ImportState: true,
484+
ImportStateVerify: true,
485+
ImportStateVerifyIgnore: []string{"deletion_window_in_days", "bypass_policy_lockout_safety_check"},
486+
},
487+
{
488+
Config: testAccKeyConfig_enabledRotationPeriod(rName),
489+
Check: resource.ComposeTestCheckFunc(
490+
testAccCheckKeyExists(ctx, resourceName, &key2),
491+
resource.TestCheckResourceAttr(resourceName, "is_enabled", "true"),
492+
resource.TestCheckResourceAttr(resourceName, "enable_key_rotation", "true"),
493+
resource.TestCheckResourceAttr(resourceName, "rotation_period_in_days", "91"),
494+
),
495+
},
496+
{
497+
Config: testAccKeyConfig_enabled(rName),
498+
Check: resource.ComposeTestCheckFunc(
499+
testAccCheckKeyExists(ctx, resourceName, &key3),
500+
resource.TestCheckResourceAttr(resourceName, "is_enabled", "true"),
501+
resource.TestCheckResourceAttr(resourceName, "enable_key_rotation", "true"),
502+
resource.TestCheckResourceAttr(resourceName, "rotation_period_in_days", "91"),
503+
),
504+
},
505+
},
506+
})
507+
}
508+
461509
func TestAccKMSKey_tags(t *testing.T) {
462510
ctx := acctest.Context(t)
463511
var key awstypes.KeyMetadata
@@ -1125,6 +1173,17 @@ resource "aws_kms_key" "test" {
11251173
`, rName)
11261174
}
11271175

1176+
func testAccKeyConfig_enabledRotationPeriod(rName string) string {
1177+
return fmt.Sprintf(`
1178+
resource "aws_kms_key" "test" {
1179+
description = %[1]q
1180+
deletion_window_in_days = 7
1181+
enable_key_rotation = true
1182+
rotation_period_in_days = "91"
1183+
}
1184+
`, rName)
1185+
}
1186+
11281187
func testAccKeyConfig_disabled(rName string) string {
11291188
return fmt.Sprintf(`
11301189
resource "aws_kms_key" "test" {

website/docs/r/kms_key.html.markdown

+2-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ The default value is `false`.
4545
If you specify a value, it must be between `7` and `30`, inclusive. If you do not specify a value, it defaults to `30`.
4646
If the KMS key is a multi-Region primary key with replicas, the waiting period begins when the last of its replica keys is deleted. Otherwise, the waiting period begins immediately.
4747
* `is_enabled` - (Optional) Specifies whether the key is enabled. Defaults to `true`.
48-
* `enable_key_rotation` - (Optional) Specifies whether [key rotation](http://docs.aws.amazon.com/kms/latest/developerguide/rotate-keys.html) is enabled. Defaults to `false`.
48+
* `enable_key_rotation` - (Optional, required to be enabled if `rotation_period_in_days` is specified) Specifies whether [key rotation](http://docs.aws.amazon.com/kms/latest/developerguide/rotate-keys.html) is enabled. Defaults to `false`.
49+
* `rotation_period_in_days` - (Optional) Custom period of time between each rotation date. Must be a number between 90 and 2560 (inclusive).
4950
* `multi_region` - (Optional) Indicates whether the KMS key is a multi-Region (`true`) or regional (`false`) key. Defaults to `false`.
5051
* `tags` - (Optional) A map of tags to assign to the object. 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.
5152
* `xks_key_id` - (Optional) Identifies the external key that serves as key material for the KMS key in an external key store.

0 commit comments

Comments
 (0)