diff --git a/packages/@aws-cdk/aws-s3tables-alpha/README.md b/packages/@aws-cdk/aws-s3tables-alpha/README.md index 2fb4986e9b3cf..42eb4a45323f9 100644 --- a/packages/@aws-cdk/aws-s3tables-alpha/README.md +++ b/packages/@aws-cdk/aws-s3tables-alpha/README.md @@ -41,6 +41,22 @@ const sampleTableBucket = new TableBucket(scope, 'ExampleTableBucket', { Learn more about table buckets maintenance operations and default behavior from the [S3 Tables User Guide](https://docs.aws.amazon.com/AmazonS3/latest/userguide/s3-table-buckets-maintenance.html) +### Server-side Encryption + +By default, S3 Tables buckets are encrypted using Amazon S3-managed keys (SSE-S3). You can also use AWS Key Management Service (AWS KMS) keys to encrypt your data. +To do this, you can specify the `kmsKey` property when creating the bucket: + +```ts +declare const kmsKey: kms.IKey; + +new TableBucket(this, 'TableBucket', { + tableBucketName: 'kms-key-s3tables-bucket', + kmsKey, +}); +``` + +**Note**: AWS CDK automatically add a resource policy to the KMS key to allow the S3 Tables service to use it for automatic table maintenance. Detail information can be found in the [security for S3 tables](https://docs.aws.amazon.com/AmazonS3/latest/userguide/s3-tables-kms-permissions.html) documentation. + ### Controlling Table Bucket Permissions ```ts diff --git a/packages/@aws-cdk/aws-s3tables-alpha/lib/table-bucket.ts b/packages/@aws-cdk/aws-s3tables-alpha/lib/table-bucket.ts index 5d35e263a9fa3..6221415aa1bcc 100644 --- a/packages/@aws-cdk/aws-s3tables-alpha/lib/table-bucket.ts +++ b/packages/@aws-cdk/aws-s3tables-alpha/lib/table-bucket.ts @@ -5,7 +5,8 @@ import { TableBucketPolicy } from './table-bucket-policy'; import * as perms from './permissions'; import { validateTableBucketAttributes } from './util'; import * as iam from 'aws-cdk-lib/aws-iam'; -import { Resource, IResource, UnscopedValidationError, RemovalPolicy, Token } from 'aws-cdk-lib/core'; +import * as kms from 'aws-cdk-lib/aws-kms'; +import { Resource, IResource, UnscopedValidationError, RemovalPolicy, Token, Stack } from 'aws-cdk-lib/core'; import { addConstructMetadata } from 'aws-cdk-lib/core/lib/metadata-resource'; /** @@ -258,6 +259,13 @@ export interface TableBucketProps { * @default RETAIN */ readonly removalPolicy?: RemovalPolicy; + + /** + * The KMS key to use for server-side encryption. + * + * @default - Server-side encryption with Amazon S3-managed keys (SSE-S3) + */ + readonly kmsKey?: kms.IKey; } /** @@ -498,8 +506,34 @@ export class TableBucket extends TableBucketBase { noncurrentDays: props.unreferencedFileRemoval?.noncurrentDays, unreferencedDays: props.unreferencedFileRemoval?.unreferencedDays, }, + encryptionConfiguration: props?.kmsKey + ? { + sseAlgorithm: 'aws:kms', + kmsKeyArn: props.kmsKey.keyArn, + } + : undefined, }); + // add resource policy to the encryption key + // see https://docs.aws.amazon.com/AmazonS3/latest/userguide/s3-tables-kms-permissions.html#tables-kms-maintenance-permissions + props?.kmsKey?.addToResourcePolicy( + new iam.PolicyStatement({ + actions: ['kms:Decrypt', 'kms:GenerateDataKey'], + resources: ['*'], + effect: iam.Effect.ALLOW, + principals: [new iam.ServicePrincipal('maintenance.s3tables.amazonaws.com')], + conditions: { + StringLike: { + 'kms:EncryptionContext:aws:s3:arn': `${Stack.of(this).formatArn({ + service: 's3tables', + resource: 'bucket', + resourceName: props.tableBucketName, + })}/*`, + }, + }, + }), + ); + this.tableBucketName = this.getResourceNameAttribute(this._resource.ref); this.tableBucketArn = this._resource.attrTableBucketArn; this._resource.applyRemovalPolicy(props.removalPolicy); diff --git a/packages/@aws-cdk/aws-s3tables-alpha/rosetta/default.ts-fixture b/packages/@aws-cdk/aws-s3tables-alpha/rosetta/default.ts-fixture index c6d93a0493b30..7f368d6acafe1 100644 --- a/packages/@aws-cdk/aws-s3tables-alpha/rosetta/default.ts-fixture +++ b/packages/@aws-cdk/aws-s3tables-alpha/rosetta/default.ts-fixture @@ -2,6 +2,7 @@ import { Construct } from 'constructs'; import { Stack } from 'aws-cdk-lib'; import { TableBucket, UnreferencedFileRemovalStatus } from '@aws-cdk/aws-s3tables-alpha'; import * as iam from 'aws-cdk-lib/aws-iam'; +import * as kms from 'aws-cdk-lib/aws-kms'; class Fixture extends Stack { constructor(scope: Construct, id: string) { diff --git a/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-kms-key.js.snapshot/cdk.out b/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-kms-key.js.snapshot/cdk.out new file mode 100644 index 0000000000000..188478b55560e --- /dev/null +++ b/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-kms-key.js.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"41.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-kms-key.js.snapshot/integ.json b/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-kms-key.js.snapshot/integ.json new file mode 100644 index 0000000000000..2c9687e9d84f7 --- /dev/null +++ b/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-kms-key.js.snapshot/integ.json @@ -0,0 +1,12 @@ +{ + "version": "41.0.0", + "testCases": { + "kms-key-s3tables-integ/DefaultTest": { + "stacks": [ + "kms-key-s3tables-stack" + ], + "assertionStack": "kms-key-s3tables-integ/DefaultTest/DeployAssert", + "assertionStackName": "kmskeys3tablesintegDefaultTestDeployAssertC8AB8C4E" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-kms-key.js.snapshot/kms-key-s3tables-stack.assets.json b/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-kms-key.js.snapshot/kms-key-s3tables-stack.assets.json new file mode 100644 index 0000000000000..560278a3536cb --- /dev/null +++ b/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-kms-key.js.snapshot/kms-key-s3tables-stack.assets.json @@ -0,0 +1,20 @@ +{ + "version": "41.0.0", + "files": { + "468631fb6c24efd89d7b1b6061454e0f86de36d40d52239419e858a45064d1a6": { + "displayName": "kms-key-s3tables-stack Template", + "source": { + "path": "kms-key-s3tables-stack.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "468631fb6c24efd89d7b1b6061454e0f86de36d40d52239419e858a45064d1a6.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-kms-key.js.snapshot/kms-key-s3tables-stack.template.json b/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-kms-key.js.snapshot/kms-key-s3tables-stack.template.json new file mode 100644 index 0000000000000..ed36584592b59 --- /dev/null +++ b/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-kms-key.js.snapshot/kms-key-s3tables-stack.template.json @@ -0,0 +1,126 @@ +{ + "Resources": { + "KmsKey46693ADD": { + "Type": "AWS::KMS::Key", + "Properties": { + "KeyPolicy": { + "Statement": [ + { + "Action": "kms:*", + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + }, + "Resource": "*" + }, + { + "Action": [ + "kms:Decrypt", + "kms:GenerateDataKey" + ], + "Condition": { + "StringLike": { + "kms:EncryptionContext:aws:s3:arn": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":s3tables:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":bucket/kms-key-s3tables-bucket/*" + ] + ] + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "maintenance.s3tables.amazonaws.com" + }, + "Resource": "*" + } + ], + "Version": "2012-10-17" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "TableBucket0BF18B0D": { + "Type": "AWS::S3Tables::TableBucket", + "Properties": { + "EncryptionConfiguration": { + "KMSKeyArn": { + "Fn::GetAtt": [ + "KmsKey46693ADD", + "Arn" + ] + }, + "SSEAlgorithm": "aws:kms" + }, + "TableBucketName": "kms-key-s3tables-bucket", + "UnreferencedFileRemoval": {} + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-kms-key.js.snapshot/kmskeys3tablesintegDefaultTestDeployAssertC8AB8C4E.assets.json b/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-kms-key.js.snapshot/kmskeys3tablesintegDefaultTestDeployAssertC8AB8C4E.assets.json new file mode 100644 index 0000000000000..3d342ddabcc26 --- /dev/null +++ b/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-kms-key.js.snapshot/kmskeys3tablesintegDefaultTestDeployAssertC8AB8C4E.assets.json @@ -0,0 +1,20 @@ +{ + "version": "41.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "displayName": "kmskeys3tablesintegDefaultTestDeployAssertC8AB8C4E Template", + "source": { + "path": "kmskeys3tablesintegDefaultTestDeployAssertC8AB8C4E.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-kms-key.js.snapshot/kmskeys3tablesintegDefaultTestDeployAssertC8AB8C4E.template.json b/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-kms-key.js.snapshot/kmskeys3tablesintegDefaultTestDeployAssertC8AB8C4E.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-kms-key.js.snapshot/kmskeys3tablesintegDefaultTestDeployAssertC8AB8C4E.template.json @@ -0,0 +1,36 @@ +{ + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-kms-key.js.snapshot/manifest.json b/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-kms-key.js.snapshot/manifest.json new file mode 100644 index 0000000000000..2c22e4084cf12 --- /dev/null +++ b/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-kms-key.js.snapshot/manifest.json @@ -0,0 +1,132 @@ +{ + "version": "42.0.0", + "artifacts": { + "kms-key-s3tables-stack.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "kms-key-s3tables-stack.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "kms-key-s3tables-stack": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "kms-key-s3tables-stack.template.json", + "terminationProtection": false, + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/468631fb6c24efd89d7b1b6061454e0f86de36d40d52239419e858a45064d1a6.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "kms-key-s3tables-stack.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "kms-key-s3tables-stack.assets" + ], + "metadata": { + "/kms-key-s3tables-stack/KmsKey": [ + { + "type": "aws:cdk:analytics:construct", + "data": "*" + } + ], + "/kms-key-s3tables-stack/KmsKey/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "KmsKey46693ADD" + } + ], + "/kms-key-s3tables-stack/TableBucket": [ + { + "type": "aws:cdk:analytics:construct", + "data": "*" + } + ], + "/kms-key-s3tables-stack/TableBucket/TableBucket": [ + { + "type": "aws:cdk:logicalId", + "data": "TableBucket0BF18B0D" + } + ], + "/kms-key-s3tables-stack/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/kms-key-s3tables-stack/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "kms-key-s3tables-stack" + }, + "kmskeys3tablesintegDefaultTestDeployAssertC8AB8C4E.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "kmskeys3tablesintegDefaultTestDeployAssertC8AB8C4E.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "kmskeys3tablesintegDefaultTestDeployAssertC8AB8C4E": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "kmskeys3tablesintegDefaultTestDeployAssertC8AB8C4E.template.json", + "terminationProtection": false, + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "kmskeys3tablesintegDefaultTestDeployAssertC8AB8C4E.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "kmskeys3tablesintegDefaultTestDeployAssertC8AB8C4E.assets" + ], + "metadata": { + "/kms-key-s3tables-integ/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/kms-key-s3tables-integ/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "kms-key-s3tables-integ/DefaultTest/DeployAssert" + }, + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + } + }, + "minimumCliVersion": "2.1006.0" +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-kms-key.js.snapshot/tree.json b/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-kms-key.js.snapshot/tree.json new file mode 100644 index 0000000000000..34ddaecda7c52 --- /dev/null +++ b/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-kms-key.js.snapshot/tree.json @@ -0,0 +1 @@ +{"version":"tree-0.1","tree":{"id":"App","path":"","children":{"kms-key-s3tables-stack":{"id":"kms-key-s3tables-stack","path":"kms-key-s3tables-stack","children":{"KmsKey":{"id":"KmsKey","path":"kms-key-s3tables-stack/KmsKey","children":{"Resource":{"id":"Resource","path":"kms-key-s3tables-stack/KmsKey/Resource","attributes":{"aws:cdk:cloudformation:type":"AWS::KMS::Key","aws:cdk:cloudformation:props":{"keyPolicy":{"Statement":[{"Action":"kms:*","Effect":"Allow","Principal":{"AWS":{"Fn::Join":["",["arn:",{"Ref":"AWS::Partition"},":iam::",{"Ref":"AWS::AccountId"},":root"]]}},"Resource":"*"},{"Action":["kms:Decrypt","kms:GenerateDataKey"],"Condition":{"StringLike":{"kms:EncryptionContext:aws:s3:arn":{"Fn::Join":["",["arn:",{"Ref":"AWS::Partition"},":s3tables:",{"Ref":"AWS::Region"},":",{"Ref":"AWS::AccountId"},":bucket/kms-key-s3tables-bucket/*"]]}}},"Effect":"Allow","Principal":{"Service":"maintenance.s3tables.amazonaws.com"},"Resource":"*"}],"Version":"2012-10-17"}}},"constructInfo":{"fqn":"aws-cdk-lib.aws_kms.CfnKey","version":"0.0.0"}}},"constructInfo":{"fqn":"aws-cdk-lib.aws_kms.Key","version":"0.0.0","metadata":["*"]}},"TableBucket":{"id":"TableBucket","path":"kms-key-s3tables-stack/TableBucket","children":{"TableBucket":{"id":"TableBucket","path":"kms-key-s3tables-stack/TableBucket/TableBucket","attributes":{"aws:cdk:cloudformation:type":"AWS::S3Tables::TableBucket","aws:cdk:cloudformation:props":{"encryptionConfiguration":{"sseAlgorithm":"aws:kms","kmsKeyArn":{"Fn::GetAtt":["KmsKey46693ADD","Arn"]}},"tableBucketName":"kms-key-s3tables-bucket","unreferencedFileRemoval":{}}},"constructInfo":{"fqn":"aws-cdk-lib.aws_s3tables.CfnTableBucket","version":"0.0.0"}}},"constructInfo":{"fqn":"@aws-cdk/aws-s3tables-alpha.TableBucket","version":"0.0.0","metadata":["*"]}},"BootstrapVersion":{"id":"BootstrapVersion","path":"kms-key-s3tables-stack/BootstrapVersion","constructInfo":{"fqn":"aws-cdk-lib.CfnParameter","version":"0.0.0"}},"CheckBootstrapVersion":{"id":"CheckBootstrapVersion","path":"kms-key-s3tables-stack/CheckBootstrapVersion","constructInfo":{"fqn":"aws-cdk-lib.CfnRule","version":"0.0.0"}}},"constructInfo":{"fqn":"aws-cdk-lib.Stack","version":"0.0.0"}},"kms-key-s3tables-integ":{"id":"kms-key-s3tables-integ","path":"kms-key-s3tables-integ","children":{"DefaultTest":{"id":"DefaultTest","path":"kms-key-s3tables-integ/DefaultTest","children":{"Default":{"id":"Default","path":"kms-key-s3tables-integ/DefaultTest/Default","constructInfo":{"fqn":"constructs.Construct","version":"10.4.2"}},"DeployAssert":{"id":"DeployAssert","path":"kms-key-s3tables-integ/DefaultTest/DeployAssert","children":{"BootstrapVersion":{"id":"BootstrapVersion","path":"kms-key-s3tables-integ/DefaultTest/DeployAssert/BootstrapVersion","constructInfo":{"fqn":"aws-cdk-lib.CfnParameter","version":"0.0.0"}},"CheckBootstrapVersion":{"id":"CheckBootstrapVersion","path":"kms-key-s3tables-integ/DefaultTest/DeployAssert/CheckBootstrapVersion","constructInfo":{"fqn":"aws-cdk-lib.CfnRule","version":"0.0.0"}}},"constructInfo":{"fqn":"aws-cdk-lib.Stack","version":"0.0.0"}}},"constructInfo":{"fqn":"@aws-cdk/integ-tests-alpha.IntegTestCase","version":"0.0.0"}}},"constructInfo":{"fqn":"@aws-cdk/integ-tests-alpha.IntegTest","version":"0.0.0"}},"Tree":{"id":"Tree","path":"Tree","constructInfo":{"fqn":"constructs.Construct","version":"10.4.2"}}},"constructInfo":{"fqn":"aws-cdk-lib.App","version":"0.0.0"}}} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-kms-key.ts b/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-kms-key.ts new file mode 100644 index 0000000000000..53fd8ce9367a4 --- /dev/null +++ b/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-kms-key.ts @@ -0,0 +1,20 @@ +import * as cdk from 'aws-cdk-lib'; +import * as integ from '@aws-cdk/integ-tests-alpha'; +import * as kms from 'aws-cdk-lib/aws-kms'; +import * as s3tables from '../../lib'; + +const app = new cdk.App(); +const stack = new cdk.Stack(app, 'kms-key-s3tables-stack'); + +const kmsKey = new kms.Key(stack, 'KmsKey'); + +new s3tables.TableBucket(stack, 'TableBucket', { + tableBucketName: 'kms-key-s3tables-bucket', + kmsKey: kmsKey, +}); + +cdk.RemovalPolicies.of(stack).destroy(); + +new integ.IntegTest(app, 'kms-key-s3tables-integ', { + testCases: [stack], +}); diff --git a/packages/@aws-cdk/aws-s3tables-alpha/test/table-bucket.test.ts b/packages/@aws-cdk/aws-s3tables-alpha/test/table-bucket.test.ts index 9863accabc4d5..224c7c3f54f6f 100644 --- a/packages/@aws-cdk/aws-s3tables-alpha/test/table-bucket.test.ts +++ b/packages/@aws-cdk/aws-s3tables-alpha/test/table-bucket.test.ts @@ -1,5 +1,6 @@ import { Template } from 'aws-cdk-lib/assertions'; import * as iam from 'aws-cdk-lib/aws-iam'; +import * as kms from 'aws-cdk-lib/aws-kms'; import * as core from 'aws-cdk-lib/core'; import * as s3tables from '../lib'; @@ -146,6 +147,94 @@ describe('TableBucket', () => { }); }); + describe('server-side encryption', () => { + test('configure customer managed key', () => { + const kmsKey = new kms.Key(stack, 'Key'); + + new s3tables.TableBucket(stack, 'ExampleTableBucket', { + tableBucketName: 'example-table-bucket', + kmsKey, + }); + + Template.fromStack(stack).hasResourceProperties('AWS::KMS::Key', { + KeyPolicy: { + Statement: [ + { + Action: 'kms:*', + Effect: 'Allow', + Principal: { + AWS: { + 'Fn::Join': [ + '', + [ + 'arn:', + { + 'Ref': 'AWS::Partition', + }, + ':iam::', + { + 'Ref': 'AWS::AccountId', + }, + ':root', + ], + ], + }, + }, + Resource: '*', + }, + { + Action: [ + 'kms:Decrypt', + 'kms:GenerateDataKey', + ], + Condition: { + StringLike: { + 'kms:EncryptionContext:aws:s3:arn': { + 'Fn::Join': [ + '', + [ + 'arn:', + { + 'Ref': 'AWS::Partition', + }, + ':s3tables:', + { + 'Ref': 'AWS::Region', + }, + ':', + { + 'Ref': 'AWS::AccountId', + }, + ':bucket/example-table-bucket/*', + ], + ], + }, + }, + }, + Effect: 'Allow', + Principal: { + Service: 'maintenance.s3tables.amazonaws.com', + }, + Resource: '*', + }, + ], + }, + }); + + Template.fromStack(stack).hasResourceProperties(TABLE_BUCKET_CFN_RESOURCE, { + 'EncryptionConfiguration': { + 'KMSKeyArn': { + 'Fn::GetAtt': [ + 'Key961B73FD', + 'Arn', + ], + }, + 'SSEAlgorithm': 'aws:kms', + }, + }); + }); + }); + describe('import existing table bucket with name', () => { const BUCKET_PROPS = { tableBucketName: 'example-table-bucket',