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

r/kms_key: add support for rotation_period_in_days #37140

Merged
merged 5 commits into from
May 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions .changelog/37140.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:enhancement
resource/aws_kms_key: Add `rotation_period_in_days` argument
```
61 changes: 40 additions & 21 deletions internal/service/kms/key.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,13 @@ func resourceKey() *schema.Resource {
return json
},
},
"rotation_period_in_days": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
ValidateFunc: validation.IntBetween(90, 2560),
RequiredWith: []string{"enable_key_rotation"},
},
names.AttrTags: tftags.TagsSchema(),
names.AttrTagsAll: tftags.TagsSchemaComputed(),
"xks_key_id": {
Expand Down Expand Up @@ -189,8 +196,8 @@ func resourceKeyCreate(ctx context.Context, d *schema.ResourceData, meta interfa

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

if enableKeyRotation := d.Get("enable_key_rotation").(bool); enableKeyRotation {
if err := updateKeyRotationEnabled(ctx, conn, "KMS Key", d.Id(), enableKeyRotation); err != nil {
if enableKeyRotation, rotationPeriod := d.Get("enable_key_rotation").(bool), d.Get("rotation_period_in_days").(int); enableKeyRotation {
if err := updateKeyRotationEnabled(ctx, conn, "KMS Key", d.Id(), enableKeyRotation, rotationPeriod); err != nil {
return sdkdiag.AppendFromErr(diags, err)
}
}
Expand Down Expand Up @@ -248,6 +255,7 @@ func resourceKeyRead(ctx context.Context, d *schema.ResourceData, meta interface
d.Set("key_id", key.metadata.KeyId)
d.Set("key_usage", key.metadata.KeyUsage)
d.Set("multi_region", key.metadata.MultiRegion)
d.Set("rotation_period_in_days", key.rotationPeriodInDays)
if key.metadata.XksKeyConfiguration != nil {
d.Set("xks_key_id", key.metadata.XksKeyConfiguration.Id)
} else {
Expand Down Expand Up @@ -279,8 +287,10 @@ func resourceKeyUpdate(ctx context.Context, d *schema.ResourceData, meta interfa
}
}

if hasChange, enable := d.HasChange("enable_key_rotation"), d.Get("enable_key_rotation").(bool); hasChange {
if err := updateKeyRotationEnabled(ctx, conn, "KMS Key", d.Id(), enable); err != nil {
if hasChange, hasChangedRotationPeriod,
enable, rotationPeriod := d.HasChange("enable_key_rotation"), d.HasChange("rotation_period_in_days"),
d.Get("enable_key_rotation").(bool), d.Get("rotation_period_in_days").(int); hasChange || (enable && hasChangedRotationPeriod) {
if err := updateKeyRotationEnabled(ctx, conn, "KMS Key", d.Id(), enable, rotationPeriod); err != nil {
return sdkdiag.AppendFromErr(diags, err)
}
}
Expand Down Expand Up @@ -344,10 +354,11 @@ func resourceKeyDelete(ctx context.Context, d *schema.ResourceData, meta interfa
}

type kmsKeyInfo struct {
metadata *awstypes.KeyMetadata
policy string
rotation *bool
tags []awstypes.Tag
metadata *awstypes.KeyMetadata
policy string
rotation *bool
rotationPeriodInDays *int32
tags []awstypes.Tag
}

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

if key.metadata.Origin == awstypes.OriginTypeAwsKms {
key.rotation, err = findKeyRotationEnabledByKeyID(ctx, conn, keyID)
key.rotation, key.rotationPeriodInDays, err = findKeyRotationEnabledByKeyID(ctx, conn, keyID)

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

func findKeyRotationEnabledByKeyID(ctx context.Context, conn *kms.Client, keyID string) (*bool, error) {
func findKeyRotationEnabledByKeyID(ctx context.Context, conn *kms.Client, keyID string) (*bool, *int32, error) {
input := &kms.GetKeyRotationStatusInput{
KeyId: aws.String(keyID),
}

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

if errs.IsA[*awstypes.NotFoundException](err) {
return nil, &retry.NotFoundError{
return nil, nil, &retry.NotFoundError{
LastError: err,
LastRequest: input,
}
}

if err != nil {
return nil, err
return nil, nil, err
}

if output == nil {
return nil, tfresource.NewEmptyResultError(input)
return nil, nil, tfresource.NewEmptyResultError(input)
}

return aws.Bool(output.KeyRotationEnabled), nil
return aws.Bool(output.KeyRotationEnabled), output.RotationPeriodInDays, nil
}

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

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

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

if enabled {
action = "enabling"
_, err = conn.EnableKeyRotation(ctx, &kms.EnableKeyRotationInput{
input := kms.EnableKeyRotationInput{
KeyId: aws.String(keyID),
})
}
if rotationPeriod > 0 {
input.RotationPeriodInDays = aws.Int32(int32(rotationPeriod))
}
_, err = conn.EnableKeyRotation(ctx, &input)
} else {
action = "disabling"
_, err = conn.DisableKeyRotation(ctx, &kms.DisableKeyRotationInput{
Expand All @@ -623,7 +638,7 @@ func updateKeyRotationEnabled(ctx context.Context, conn *kms.Client, resourceTyp
}

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

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

func waitKeyRotationEnabledPropagated(ctx context.Context, conn *kms.Client, keyID string, enabled bool) error {
func waitKeyRotationEnabledPropagated(ctx context.Context, conn *kms.Client, keyID string, enabled bool, rotationPeriodWant int) error {
checkFunc := func() (bool, error) {
output, err := findKeyRotationEnabledByKeyID(ctx, conn, keyID)
rotation, rotationPeriodGot, err := findKeyRotationEnabledByKeyID(ctx, conn, keyID)

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

return aws.ToBool(output) == enabled, nil
if rotationPeriodWant != 0 && rotationPeriodGot != nil {
return aws.ToBool(rotation) == enabled && aws.ToInt32(rotationPeriodGot) == int32(rotationPeriodWant), nil
}

return aws.ToBool(rotation) == enabled, nil
}
opts := tfresource.WaitOpts{
ContinuousTargetOccurence: 5,
Expand Down
59 changes: 59 additions & 0 deletions internal/service/kms/key_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,54 @@ func TestAccKMSKey_isEnabled(t *testing.T) {
})
}

func TestAccKMSKey_rotation(t *testing.T) {
ctx := acctest.Context(t)
var key1, key2, key3 awstypes.KeyMetadata
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
resourceName := "aws_kms_key.test"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(ctx, t) },
ErrorCheck: acctest.ErrorCheck(t, names.KMSServiceID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
CheckDestroy: testAccCheckKeyDestroy(ctx),
Steps: []resource.TestStep{
{
Config: testAccKeyConfig_enabledRotation(rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckKeyExists(ctx, resourceName, &key1),
resource.TestCheckResourceAttr(resourceName, "is_enabled", "true"),
resource.TestCheckResourceAttr(resourceName, "enable_key_rotation", "true"),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"deletion_window_in_days", "bypass_policy_lockout_safety_check"},
},
{
Config: testAccKeyConfig_enabledRotationPeriod(rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckKeyExists(ctx, resourceName, &key2),
resource.TestCheckResourceAttr(resourceName, "is_enabled", "true"),
resource.TestCheckResourceAttr(resourceName, "enable_key_rotation", "true"),
resource.TestCheckResourceAttr(resourceName, "rotation_period_in_days", "91"),
),
},
{
Config: testAccKeyConfig_enabled(rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckKeyExists(ctx, resourceName, &key3),
resource.TestCheckResourceAttr(resourceName, "is_enabled", "true"),
resource.TestCheckResourceAttr(resourceName, "enable_key_rotation", "true"),
resource.TestCheckResourceAttr(resourceName, "rotation_period_in_days", "91"),
),
},
},
})
}

func TestAccKMSKey_tags(t *testing.T) {
ctx := acctest.Context(t)
var key awstypes.KeyMetadata
Expand Down Expand Up @@ -1125,6 +1173,17 @@ resource "aws_kms_key" "test" {
`, rName)
}

func testAccKeyConfig_enabledRotationPeriod(rName string) string {
return fmt.Sprintf(`
resource "aws_kms_key" "test" {
description = %[1]q
deletion_window_in_days = 7
enable_key_rotation = true
rotation_period_in_days = "91"
}
`, rName)
}

func testAccKeyConfig_disabled(rName string) string {
return fmt.Sprintf(`
resource "aws_kms_key" "test" {
Expand Down
3 changes: 2 additions & 1 deletion website/docs/r/kms_key.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ The default value is `false`.
If you specify a value, it must be between `7` and `30`, inclusive. If you do not specify a value, it defaults to `30`.
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.
* `is_enabled` - (Optional) Specifies whether the key is enabled. Defaults to `true`.
* `enable_key_rotation` - (Optional) Specifies whether [key rotation](http://docs.aws.amazon.com/kms/latest/developerguide/rotate-keys.html) is enabled. Defaults to `false`.
* `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`.
* `rotation_period_in_days` - (Optional) Custom period of time between each rotation date. Must be a number between 90 and 2560 (inclusive).
* `multi_region` - (Optional) Indicates whether the KMS key is a multi-Region (`true`) or regional (`false`) key. Defaults to `false`.
* `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.
* `xks_key_id` - (Optional) Identifies the external key that serves as key material for the KMS key in an external key store.
Expand Down
Loading