Repository: turbot/steampipe-plugin-aws (language: go)

Issue:
Add table aws_kms_key_rotation
**References**
https://aws.amazon.com/about-aws/whats-new/2024/04/aws-kms-automatic-key-rotation/

Specifically
> Lastly, you can now see the history of all previous rotations for any KMS key that has been rotated. 

I believe it's here:
https://pkg.go.dev/github.com/aws/aws-sdk-go/service/kms#RotationsListEntry

I may take a crack at it if I have time, but, it's unlikely I'll have time super soon.




Discussion / hints:

Fix patch:
diff --git a/aws/plugin.go b/aws/plugin.go
index 823bd8f65..d0e481fa7 100644
--- a/aws/plugin.go
+++ b/aws/plugin.go
@@ -355,6 +355,7 @@ func Plugin(ctx context.Context) *plugin.Plugin {
 			"aws_kinesisanalyticsv2_application":                           tableAwsKinesisAnalyticsV2Application(ctx),
 			"aws_kms_alias":                                                tableAwsKmsAlias(ctx),
 			"aws_kms_key":                                                  tableAwsKmsKey(ctx),
+			"aws_kms_key_rotation":                                         tableAwsKmsKeyRotation(ctx),
 			"aws_lambda_alias":                                             tableAwsLambdaAlias(ctx),
 			"aws_lambda_event_source_mapping":                              tableAwsLambdaEventSourceMapping(ctx),
 			"aws_lambda_function":                                          tableAwsLambdaFunction(ctx),
diff --git a/aws/table_aws_kms_key_rotation.go b/aws/table_aws_kms_key_rotation.go
new file mode 100644
index 000000000..860364599
--- /dev/null
+++ b/aws/table_aws_kms_key_rotation.go
@@ -0,0 +1,161 @@
+package aws
+
+import (
+	"context"
+	"strings"
+	"time"
+
+	"github.com/aws/aws-sdk-go-v2/aws"
+	"github.com/aws/aws-sdk-go-v2/service/kms"
+	"github.com/aws/aws-sdk-go-v2/service/kms/types"
+
+	kmsv1 "github.com/aws/aws-sdk-go/service/kms"
+
+	"github.com/turbot/steampipe-plugin-sdk/v5/grpc/proto"
+	"github.com/turbot/steampipe-plugin-sdk/v5/plugin"
+	"github.com/turbot/steampipe-plugin-sdk/v5/plugin/transform"
+)
+
+//// TABLE DEFINITION
+
+func tableAwsKmsKeyRotation(_ context.Context) *plugin.Table {
+	return &plugin.Table{
+		Name:        "aws_kms_key_rotation",
+		Description: "AWS KMS Key Rotation",
+		List: &plugin.ListConfig{
+			ParentHydrate: listKmsKeys,
+			Hydrate:       listKmsKeyRotations,
+			IgnoreConfig: &plugin.IgnoreConfig{
+				ShouldIgnoreErrorFunc: shouldIgnoreErrors([]string{"NotFoundException"}),
+			},
+			Tags: map[string]string{"service": "kms", "action": "ListKeyRotations"},
+			KeyColumns: plugin.KeyColumnSlice{
+				{
+					Name:    "key_id",
+					Require: plugin.Optional,
+				},
+				{
+					Name:    "key_arn",
+					Require: plugin.Optional,
+				},
+			},
+		},
+		GetMatrixItemFunc: SupportedRegionMatrix(kmsv1.EndpointsID),
+		Columns: awsRegionalColumns([]*plugin.Column{
+			{
+				Name:        "key_id",
+				Description: "Unique identifier of the key.",
+				Type:        proto.ColumnType_STRING,
+			},
+			{
+				Name:        "key_arn",
+				Description: "ARN of the key.",
+				Type:        proto.ColumnType_STRING,
+			},
+			{
+				Name:        "rotation_type",
+				Description: "Identifies whether the key material rotation was a scheduled automatic rotation or an on-demand rotation.",
+				Type:        proto.ColumnType_STRING,
+			},
+			{
+				Name:        "rotation_date",
+				Description: "Date and time that the key material rotation completed.",
+				Type:        proto.ColumnType_TIMESTAMP,
+			},
+
+			/// Standard columns for all tables
+			{
+				Name:        "title",
+				Description: resourceInterfaceDescription("title"),
+				Type:        proto.ColumnType_STRING,
+				Transform:   transform.FromField("KeyId"),
+			},
+		}),
+	}
+}
+
+type RotationInfo struct {
+	KeyId        *string
+	KeyArn       *string
+	RotationDate *time.Time
+	RotationType types.RotationType
+}
+
+//// LIST FUNCTION
+
+func listKmsKeyRotations(ctx context.Context, d *plugin.QueryData, h *plugin.HydrateData) (interface{}, error) {
+	var key types.KeyListEntry
+	if h.Item != nil {
+		key = h.Item.(types.KeyListEntry)
+	}
+
+	if d.EqualsQualString("key_id") != "" || d.EqualsQualString("key_arn") != "" {
+		if d.EqualsQualString("key_id") != "" && d.EqualsQualString("key_id") != *key.KeyId {
+			return nil, nil
+		}
+		if d.EqualsQualString("key_arn") != "" && d.EqualsQualString("key_arn") != *key.KeyArn {
+			return nil, nil
+		}
+	}
+
+	// Create Session
+	svc, err := KMSClient(ctx, d)
+	if err != nil {
+		plugin.Logger(ctx).Error("aws_kms_key_rotation.listKmsKeyRotations", "connection_error", err)
+		return nil, err
+	}
+
+	if svc == nil {
+		// Unsupported region check
+		return nil, nil
+	}
+
+	maxItems := int32(1000)
+	input := &kms.ListKeyRotationsInput{
+		KeyId: key.KeyArn,
+	}
+
+	// Reduce the basic request limit down if the user has only requested a small number of rows
+	if d.QueryContext.Limit != nil {
+		limit := int32(*d.QueryContext.Limit)
+		if limit < maxItems {
+			if limit < 1 {
+				maxItems = int32(1)
+			} else {
+				maxItems = int32(limit)
+			}
+		}
+	}
+	input.Limit = aws.Int32(maxItems)
+	paginator := kms.NewListKeyRotationsPaginator(svc, input, func(o *kms.ListKeyRotationsPaginatorOptions) {
+		o.Limit = maxItems
+		o.StopOnDuplicateToken = true
+	})
+
+	for paginator.HasMorePages() {
+		// apply rate limiting
+		d.WaitForListRateLimit(ctx)
+
+		output, err := paginator.NextPage(ctx)
+		if err != nil {
+			plugin.Logger(ctx).Error("aws_kms_key_rotation.listKmsKeyRotations", "api_error", err)
+			return nil, err
+		}
+
+		for _, rotation := range output.Rotations {
+			d.StreamListItem(ctx, &RotationInfo{
+				KeyId:        aws.String(strings.Split(*rotation.KeyId, "/")[1]),
+				KeyArn:       rotation.KeyId,
+				RotationDate: rotation.RotationDate,
+				RotationType: rotation.RotationType,
+			})
+
+			// Context may get cancelled due to manual cancellation or if the limit has been reached
+			if d.RowsRemaining(ctx) == 0 {
+				return nil, nil
+			}
+		}
+	}
+
+	return nil, nil
+}
diff --git a/docs/tables/aws_kms_key_rotation.md b/docs/tables/aws_kms_key_rotation.md
new file mode 100644
index 000000000..9d3b4ffec
--- /dev/null
+++ b/docs/tables/aws_kms_key_rotation.md
@@ -0,0 +1,118 @@
+---
+title: "Steampipe Table: aws_kms_key_rotation - Query AWS KMS Key Rotation using SQL"
+description: "Allows users to query AWS KMS Key Rotation data including the rotation schedule, type, and associated key details."
+---
+
+# Table: aws_kms_key_rotation - Query AWS KMS Key Rotation using SQL
+
+The AWS Key Management Service (KMS) includes functionalities for rotating encryption keys, which is crucial for maintaining the security of cryptographic keys over time. The `aws_kms_key_rotation` table provides access to detailed information about the rotation status and history of these keys, enabling enhanced security management and compliance with best practices.
+
+## Table Usage Guide
+
+The `aws_kms_key_rotation` table in Steampipe is useful for security analysts and DevOps engineers to monitor and audit the rotation of AWS KMS keys. It includes key details such as the rotation date, type, and associated key ARN. This table allows you to query information efficiently for regular audits and compliance reporting.
+
+## Examples
+
+### Basic info
+Retrieve basic information about key rotations including ARN, rotation date, and type. This can be useful for regular audits of key management practices.
+
+```sql+postgres
+select
+  key_id,
+  key_arn,
+  rotation_date,
+  rotation_type
+from
+  aws_kms_key_rotation;
+```
+
+```sql+sqlite
+select
+  key_id,
+  key_arn,
+  rotation_date,
+  rotation_type
+from
+  aws_kms_key_rotation;
+```
+
+### Keys with recent rotations
+List details of keys that have undergone rotation within the last 30 days, helping to ensure recent key rotations are tracked for security compliance.
+
+```sql+postgres
+select
+  key_id,
+  key_arn,
+  rotation_date
+from
+  aws_kms_key_rotation
+where
+  rotation_date >= current_date - interval '30 days';
+```
+
+```sql+sqlite
+select
+  key_id,
+  key_arn,
+  rotation_date
+from
+  aws_kms_key_rotation
+where
+  strftime('%s', 'now') - strftime('%s', rotation_date) <= 2592000;
+```
+
+### Join with aws_kms_key to get complete key details
+Provide a comprehensive overview of key rotation along with key management details.
+
+```sql+postgres
+select
+  akr.key_id,
+  ak.title,
+  akr.rotation_date,
+  akr.rotation_type,
+  ak.key_manager
+from
+  aws_kms_key_rotation akr
+join
+  aws_kms_key ak
+on
+  akr.key_id = ak.id;
+```
+
+```sql+sqlite
+select
+  akr.key_id,
+  ak.title,
+  akr.rotation_date,
+  akr.rotation_type,
+  ak.key_manager
+from
+  aws_kms_key_rotation akr
+join
+  aws_kms_key ak
+on
+  akr.key_id = ak.id;
+```
+
+### Count of key rotations by type
+This query groups keys by rotation type, providing insights into how many keys are rotated automatically versus on-demand.
+
+```sql+postgres
+select
+  rotation_type,
+  count(key_id) as count
+from
+  aws_kms_key_rotation
+group by
+  rotation_type;
+```
+
+```sql+sqlite
+select
+  rotation_type,
+  count(key_id) as count
+from
+  aws_kms_key_rotation
+group by
+  rotation_type;
+```
\ No newline at end of file
diff --git a/go.mod b/go.mod
index 6e29ba254..9095c9909 100644
--- a/go.mod
+++ b/go.mod
@@ -77,7 +77,7 @@ require (
 	github.com/aws/aws-sdk-go-v2/service/kinesis v1.27.4
 	github.com/aws/aws-sdk-go-v2/service/kinesisanalyticsv2 v1.25.2
 	github.com/aws/aws-sdk-go-v2/service/kinesisvideo v1.23.4
-	github.com/aws/aws-sdk-go-v2/service/kms v1.30.1
+	github.com/aws/aws-sdk-go-v2/service/kms v1.31.0
 	github.com/aws/aws-sdk-go-v2/service/lakeformation v1.31.5
 	github.com/aws/aws-sdk-go-v2/service/lambda v1.54.0
 	github.com/aws/aws-sdk-go-v2/service/lightsail v1.37.0
diff --git a/go.sum b/go.sum
index adaac4760..a165a8129 100644
--- a/go.sum
+++ b/go.sum
@@ -203,8 +203,6 @@ github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kd
 github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY=
 github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4=
 github.com/aws/aws-sdk-go v1.44.122/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
-github.com/aws/aws-sdk-go v1.44.189 h1:9PBrjndH1uL5AN8818qI3duhQ4hgkMuLvqkJlg9MRyk=
-github.com/aws/aws-sdk-go v1.44.189/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
 github.com/aws/aws-sdk-go v1.51.19 h1:jp/Vx/mUpXttthvvo/4/Nn/3+zumirIlAFkp1Irf1kM=
 github.com/aws/aws-sdk-go v1.51.19/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
 github.com/aws/aws-sdk-go-v2 v1.26.1 h1:5554eUqIYVWpU0YmeeYZ0wU64H2VLBs8TlhRB2L+EkA=
@@ -373,6 +371,8 @@ github.com/aws/aws-sdk-go-v2/service/kinesisvideo v1.23.4 h1:MncIhVIHv+hKmhzOQz5
 github.com/aws/aws-sdk-go-v2/service/kinesisvideo v1.23.4/go.mod h1:SP1vzJ1AN2/JQ7S66QgIda+rVPSxZSt/y9LDHtk6E1k=
 github.com/aws/aws-sdk-go-v2/service/kms v1.30.1 h1:SBn4I0fJXF9FYOVRSVMWuhvEKoAHDikjGpS3wlmw5DE=
 github.com/aws/aws-sdk-go-v2/service/kms v1.30.1/go.mod h1:2snWQJQUKsbN66vAawJuOGX7dr37pfOq9hb0tZDGIqQ=
+github.com/aws/aws-sdk-go-v2/service/kms v1.31.0 h1:yl7wcqbisxPzknJVfWTLnK83McUvXba+pz2+tPbIUmQ=
+github.com/aws/aws-sdk-go-v2/service/kms v1.31.0/go.mod h1:2snWQJQUKsbN66vAawJuOGX7dr37pfOq9hb0tZDGIqQ=
 github.com/aws/aws-sdk-go-v2/service/lakeformation v1.31.5 h1:HUg52pxsqXCGJRNOLkCDx6Sm6hcKA3CU6cl83gqBNtE=
 github.com/aws/aws-sdk-go-v2/service/lakeformation v1.31.5/go.mod h1:0xTSto0XwDuPvY7P3XoEwOLH7sr5EzehNvxCoBaeuPU=
 github.com/aws/aws-sdk-go-v2/service/lambda v1.54.0 h1:gazALVrZ7RIG6gJXut3c7NKtPgs9eQ8BFCA9uoliayk=


Test patch: