From 5b8574ea16963b014b14470d021de4d539040385 Mon Sep 17 00:00:00 2001 From: Soham Kulkarni Date: Thu, 24 Apr 2025 16:01:39 -0700 Subject: [PATCH 1/7] Add KMS support for TableBucket --- .../aws-s3tables-alpha/lib/permissions.ts | 16 ++ .../aws-s3tables-alpha/lib/table-bucket.ts | 200 +++++++++++++++++- 2 files changed, 209 insertions(+), 7 deletions(-) diff --git a/packages/@aws-cdk/aws-s3tables-alpha/lib/permissions.ts b/packages/@aws-cdk/aws-s3tables-alpha/lib/permissions.ts index a9d205bdbc737..67c0c2552c3f3 100644 --- a/packages/@aws-cdk/aws-s3tables-alpha/lib/permissions.ts +++ b/packages/@aws-cdk/aws-s3tables-alpha/lib/permissions.ts @@ -20,3 +20,19 @@ export const TABLE_BUCKET_READ_WRITE_ACCESS = [...new Set([ ...TABLE_BUCKET_READ_ACCESS, ...TABLE_BUCKET_WRITE_ACCESS, ])]; + +// Permissions for user defined KMS Keys +// https://docs.aws.amazon.com/AmazonS3/latest/userguide/s3-tables-kms-permissions.html +export const KEY_READ_ACCESS = [ + 'kms:Decrypt', +]; + +export const KEY_WRITE_ACCESS = [ + 'kms:GenerateDataKey*', + 'kms:Decrypt', +]; + +export const KEY_READ_WRITE_ACCESS = [...new Set([ + ...KEY_READ_ACCESS, + ...KEY_WRITE_ACCESS, +])]; 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..00f845af10f66 100644 --- a/packages/@aws-cdk/aws-s3tables-alpha/lib/table-bucket.ts +++ b/packages/@aws-cdk/aws-s3tables-alpha/lib/table-bucket.ts @@ -5,6 +5,7 @@ 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 * as kms from 'aws-cdk-lib/aws-kms'; import { Resource, IResource, UnscopedValidationError, RemovalPolicy, Token } from 'aws-cdk-lib/core'; import { addConstructMetadata } from 'aws-cdk-lib/core/lib/metadata-resource'; @@ -36,11 +37,15 @@ export interface ITableBucket extends IResource { */ readonly region?: string; + /** + * Optional KMS encryption key associated with this table bucket. + */ + readonly encryptionKey?: kms.IKey; + /** * Adds a statement to the resource policy for a principal (i.e. * account/role/service) to perform actions on this table bucket and/or its - * contents. Use `tableBucketArn` and `arnForObjects(keys)` to obtain ARNs for - * this bucket or objects. + * tables. * * Note that the policy statement may or may not be added to the policy. * For example, when an `ITableBucket` is created from an existing table bucket, @@ -62,6 +67,9 @@ export interface ITableBucket extends IResource { * Grant read permissions for this table bucket and its tables * to an IAM principal (Role/Group/User). * + * If encryption is used, permission to use the key to decrypt the contents + * of the bucket will also be granted to the same principal. + * * @param identity The principal to allow read permissions to * @param tableId Allow the permissions to all tables using '*' or to single table by its unique ID. */ @@ -71,6 +79,9 @@ export interface ITableBucket extends IResource { * Grant write permissions for this table bucket and its tables * to an IAM principal (Role/Group/User). * + * If encryption is used, permission to use the key to encrypt the contents + * of the bucket will also be granted to the same principal. + * * @param identity The principal to allow write permissions to * @param tableId Allow the permissions to all tables using '*' or to single table by its unique ID. */ @@ -80,6 +91,9 @@ export interface ITableBucket extends IResource { * Grant read and write permissions for this table bucket and its tables * to an IAM principal (Role/Group/User). * + * If encryption is used, permission to use the key to encrypt/decrypt the contents + * of the bucket will also be granted to the same principal. + * * @param identity The principal to allow read and write permissions to * @param tableId Allow the permissions to all tables using '*' or to single table by its unique ID. */ @@ -130,6 +144,19 @@ export enum UnreferencedFileRemovalStatus { DISABLED = 'Disabled', } +export enum TableBucketEncryption { + /** + * Use a customer defined KMS key for encryption + * If `encryptionKey` is specified, this key will be used, otherwise, one will be defined. + */ + KMS = 'aws:kms', + + /** + * Use S3 managed encryption keys with AES256 encryption + */ + S3_MANAGED = 'AES256', +} + abstract class TableBucketBase extends Resource implements ITableBucket { public abstract readonly tableBucketArn: string; public abstract readonly tableBucketName: string; @@ -148,6 +175,8 @@ abstract class TableBucketBase extends Resource implements ITableBucket { */ protected abstract autoCreatePolicy: boolean; + public abstract encryptionKey?: kms.IKey | undefined; + /** * Adds a statement to the resource policy for a principal (i.e. * account/role/service) to perform actions on this table bucket and/or its @@ -186,15 +215,33 @@ abstract class TableBucketBase extends Resource implements ITableBucket { } public grantRead(identity: iam.IGrantable, tableId: string) { - return this.grant(identity, perms.TABLE_BUCKET_READ_ACCESS, this.tableBucketArn, this.getTableArn(tableId)); + return this.grant( + identity, + perms.TABLE_BUCKET_READ_ACCESS, + perms.KEY_READ_ACCESS, + this.tableBucketArn, + this.getTableArn(tableId), + ); } public grantWrite(identity: iam.IGrantable, tableId: string) { - return this.grant(identity, perms.TABLE_BUCKET_WRITE_ACCESS, this.tableBucketArn, this.getTableArn(tableId)); + return this.grant( + identity, + perms.TABLE_BUCKET_WRITE_ACCESS, + perms.KEY_WRITE_ACCESS, + this.tableBucketArn, + this.getTableArn(tableId), + ); } public grantReadWrite(identity: iam.IGrantable, tableId: string) { - return this.grant(identity, perms.TABLE_BUCKET_READ_WRITE_ACCESS, this.tableBucketArn, this.getTableArn(tableId)); + return this.grant( + identity, + perms.TABLE_BUCKET_READ_WRITE_ACCESS, + perms.KEY_WRITE_ACCESS, + this.tableBucketArn, + this.getTableArn(tableId), + ); } /** @@ -204,15 +251,23 @@ abstract class TableBucketBase extends Resource implements ITableBucket { private grant( grantee: iam.IGrantable, tableBucketActions: string[], + keyActions: string[], resourceArn: string, ...otherResourceArns: (string | undefined)[]) { const resources = [resourceArn, ...otherResourceArns].filter(arn => arn != undefined); - return iam.Grant.addToPrincipalOrResource({ + + const grant = iam.Grant.addToPrincipalOrResource({ grantee, actions: tableBucketActions, resourceArns: resources, resource: this, }); + + if (this.encryptionKey && keyActions && keyActions.length !== 0) { + this.encryptionKey.grant(grantee, ...keyActions); + } + + return grant; } private getTableArn(tableId: string | undefined) { @@ -252,6 +307,27 @@ export interface TableBucketProps { */ readonly account?: string; + /** + * The kind of server-side encryption to apply to this bucket. + * + * If you choose KMS, you can specify a KMS key via `encryptionKey`. If + * encryption key is not specified, a key will automatically be created. + * + * @default - `KMS` if `encryptionKey` is specified, or `S3_MANAGED` otherwise. + */ + readonly encryption?: TableBucketEncryption; + + /** + * External KMS key to use for bucket encryption. + * + * The `encryption` property must be either not specified or set to `KMS`. + * An error will be emitted if `encryption` is set to `S3_MANAGED`. + * + * @default - If `encryption` is set to `KMS` and this property is undefined, + * a new KMS key will be created and associated with this bucket. + */ + readonly encryptionKey?: kms.IKey; + /** * Controls what happens to this table bucket it it stoped being managed by cloudformation. * @@ -261,7 +337,8 @@ export interface TableBucketProps { } /** - * Everything needed to reference a specific table bucket. + * A reference to a table bucket outside this stack + * * The tableBucketName, region, and account can be provided explicitly * or will be inferred from the tableBucketArn */ @@ -271,21 +348,30 @@ export interface TableBucketAttributes { * @default region inferred from scope */ readonly region?: string; + /** * The accountId containing this table bucket * @default account inferred from scope */ readonly account?: string; + /** * The table bucket name, unique per region * @default tableBucketName inferred from arn */ readonly tableBucketName?: string; + /** * The table bucket's ARN. * @default tableBucketArn constructed from region, account and tableBucketName are provided */ readonly tableBucketArn?: string; + + /** + * Optional KMS encryption key associated with this bucket. + * @default - undefined + */ + readonly encryptionKey?: kms.IKey; } /** @@ -337,6 +423,7 @@ export class TableBucket extends TableBucketBase { public readonly tableBucketPolicy?: TableBucketPolicy; public readonly region = region; public readonly account = account; + public readonly encryptionKey?: kms.IKey | undefined; protected autoCreatePolicy: boolean = false; /** @@ -478,6 +565,8 @@ export class TableBucket extends TableBucketBase { */ public readonly tableBucketName: string; + public readonly encryptionKey?: kms.IKey | undefined; + protected autoCreatePolicy: boolean = true; constructor(scope: Construct, id: string, props: TableBucketProps) { @@ -490,6 +579,7 @@ export class TableBucket extends TableBucketBase { TableBucket.validateTableBucketName(props.tableBucketName); TableBucket.validateUnreferencedFileRemoval(props.unreferencedFileRemoval); + const { bucketEncryption, encryptionKey, keyCreated } = this.parseEncryption(props); this._resource = new s3tables.CfnTableBucket(this, id, { tableBucketName: props.tableBucketName, @@ -498,11 +588,107 @@ export class TableBucket extends TableBucketBase { noncurrentDays: props.unreferencedFileRemoval?.noncurrentDays, unreferencedDays: props.unreferencedFileRemoval?.unreferencedDays, }, + encryptionConfiguration: bucketEncryption, }); this.tableBucketName = this.getResourceNameAttribute(this._resource.ref); this.tableBucketArn = this._resource.attrTableBucketArn; this._resource.applyRemovalPolicy(props.removalPolicy); + + if (keyCreated && encryptionKey) { + this.allowMaintenanceAccessToKey(encryptionKey); + } + } + + /** + * Set up key properties and return the Bucket encryption property from the + * user's configuration, according to the following table: + * + * | props.encryption | props.encryptionKey | bucketEncryption (return value) | encryptionKey (return value) | + * |------------------|---------------------|---------------------------------|-------------------------------| + * | undefined | undefined | undefined | undefined | + * | undefined | k | aws:kms | k | + * | KMS | undefined | aws:kms | new key (allow maintenance SP)| + * | KMS | k | aws:kms | k | + * | S3_MANAGED | undefined | AES256 | undefined | + * | S3_MANAGED | k | ERROR! | ERROR! | + */ + private parseEncryption(props: TableBucketProps): { + bucketEncryption?: s3tables.CfnTableBucket.EncryptionConfigurationProperty; + encryptionKey?: kms.IKey; + keyCreated?: boolean; + } { + const encryptionType = props.encryption; + let key = props.encryptionKey; + + if (encryptionType === undefined) { + if ( key === undefined ) { + return { bucketEncryption: undefined, encryptionKey: undefined }; + } else { + return { + bucketEncryption: { + kmsKeyArn: key.keyArn, + sseAlgorithm: TableBucketEncryption.KMS, + }, + encryptionKey: key, + }; + } + } + + if (encryptionType === TableBucketEncryption.KMS) { + if ( key === undefined ) { + key = new kms.Key(this, 'Key', { + description: `Created by ${this.node.path}`, + enableKeyRotation: true, + }); + } + return { + bucketEncryption: { + kmsKeyArn: key.keyArn, + sseAlgorithm: TableBucketEncryption.KMS, + }, + encryptionKey: key, + }; + } + + if (encryptionType === TableBucketEncryption.S3_MANAGED) { + if ( key === undefined ) { + return { + bucketEncryption: { + sseAlgorithm: TableBucketEncryption.S3_MANAGED, + }, + }; + } else { + throw new UnscopedValidationError('Expected encryption = `KMS` with user provided encryption key'); + } + } + throw new UnscopedValidationError('Expected encryption = `KMS` with user provided encryption key'); + } + + /** + * Allowlist S3 Tables Maintenance to access this table bucket's encryption key + * + * @see https://docs.aws.amazon.com/AmazonS3/latest/userguide/s3-tables-kms-permissions.html + * @param encryptionKey The key to provide access to + */ + private allowMaintenanceAccessToKey(encryptionKey: kms.IKey) { + encryptionKey.addToResourcePolicy(new iam.PolicyStatement({ + sid: 'AllowS3TablesMaintenanceAccess', + effect: iam.Effect.ALLOW, + principals: [ + new iam.ServicePrincipal('maintenance.s3tables.amazonaws.com'), + ], + actions: [ + 'kms:GenerateDataKey', + 'kms:Decrypt', + ], + resources: [encryptionKey.keyArn], + conditions: { + StringLike: { + 'kms:EncryptionContext:aws:s3:arn': `${this.tableBucketArn}/*`, + }, + }, + })); } } From a2fc86a092e1fc7ddc5159e5aa8243223716f552 Mon Sep 17 00:00:00 2001 From: Soham Kulkarni Date: Sun, 27 Apr 2025 16:51:33 -0700 Subject: [PATCH 2/7] Add unit test coverage for KMS support --- .../aws-s3tables-alpha/lib/permissions.ts | 2 +- .../aws-s3tables-alpha/lib/table-bucket.ts | 21 +- .../test/table-bucket-encryption.test.ts | 395 ++++++++++++++++++ .../test/table-bucket.test.ts | 2 +- .../aws-s3tables-alpha/test/test-utils.ts | 6 + 5 files changed, 411 insertions(+), 15 deletions(-) create mode 100644 packages/@aws-cdk/aws-s3tables-alpha/test/table-bucket-encryption.test.ts create mode 100644 packages/@aws-cdk/aws-s3tables-alpha/test/test-utils.ts diff --git a/packages/@aws-cdk/aws-s3tables-alpha/lib/permissions.ts b/packages/@aws-cdk/aws-s3tables-alpha/lib/permissions.ts index 67c0c2552c3f3..b14e01a3ceee3 100644 --- a/packages/@aws-cdk/aws-s3tables-alpha/lib/permissions.ts +++ b/packages/@aws-cdk/aws-s3tables-alpha/lib/permissions.ts @@ -28,8 +28,8 @@ export const KEY_READ_ACCESS = [ ]; export const KEY_WRITE_ACCESS = [ - 'kms:GenerateDataKey*', 'kms:Decrypt', + 'kms:GenerateDataKey*', ]; export const KEY_READ_WRITE_ACCESS = [...new Set([ 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 00f845af10f66..c793ce7f56c46 100644 --- a/packages/@aws-cdk/aws-s3tables-alpha/lib/table-bucket.ts +++ b/packages/@aws-cdk/aws-s3tables-alpha/lib/table-bucket.ts @@ -144,6 +144,9 @@ export enum UnreferencedFileRemovalStatus { DISABLED = 'Disabled', } +/** + * Controls Server Side Encryption (SSE) for this TableBucket. + */ export enum TableBucketEncryption { /** * Use a customer defined KMS key for encryption @@ -579,7 +582,8 @@ export class TableBucket extends TableBucketBase { TableBucket.validateTableBucketName(props.tableBucketName); TableBucket.validateUnreferencedFileRemoval(props.unreferencedFileRemoval); - const { bucketEncryption, encryptionKey, keyCreated } = this.parseEncryption(props); + const { bucketEncryption, encryptionKey } = this.parseEncryption(props); + this.encryptionKey = encryptionKey; this._resource = new s3tables.CfnTableBucket(this, id, { tableBucketName: props.tableBucketName, @@ -594,10 +598,6 @@ export class TableBucket extends TableBucketBase { this.tableBucketName = this.getResourceNameAttribute(this._resource.ref); this.tableBucketArn = this._resource.attrTableBucketArn; this._resource.applyRemovalPolicy(props.removalPolicy); - - if (keyCreated && encryptionKey) { - this.allowMaintenanceAccessToKey(encryptionKey); - } } /** @@ -616,7 +616,6 @@ export class TableBucket extends TableBucketBase { private parseEncryption(props: TableBucketProps): { bucketEncryption?: s3tables.CfnTableBucket.EncryptionConfigurationProperty; encryptionKey?: kms.IKey; - keyCreated?: boolean; } { const encryptionType = props.encryption; let key = props.encryptionKey; @@ -641,6 +640,7 @@ export class TableBucket extends TableBucketBase { description: `Created by ${this.node.path}`, enableKeyRotation: true, }); + this.allowTablesMaintenanceAccessToKey(key); } return { bucketEncryption: { @@ -671,7 +671,7 @@ export class TableBucket extends TableBucketBase { * @see https://docs.aws.amazon.com/AmazonS3/latest/userguide/s3-tables-kms-permissions.html * @param encryptionKey The key to provide access to */ - private allowMaintenanceAccessToKey(encryptionKey: kms.IKey) { + private allowTablesMaintenanceAccessToKey(encryptionKey: kms.IKey) { encryptionKey.addToResourcePolicy(new iam.PolicyStatement({ sid: 'AllowS3TablesMaintenanceAccess', effect: iam.Effect.ALLOW, @@ -682,12 +682,7 @@ export class TableBucket extends TableBucketBase { 'kms:GenerateDataKey', 'kms:Decrypt', ], - resources: [encryptionKey.keyArn], - conditions: { - StringLike: { - 'kms:EncryptionContext:aws:s3:arn': `${this.tableBucketArn}/*`, - }, - }, + resources: ['*'], })); } } diff --git a/packages/@aws-cdk/aws-s3tables-alpha/test/table-bucket-encryption.test.ts b/packages/@aws-cdk/aws-s3tables-alpha/test/table-bucket-encryption.test.ts new file mode 100644 index 0000000000000..68eb84d845c36 --- /dev/null +++ b/packages/@aws-cdk/aws-s3tables-alpha/test/table-bucket-encryption.test.ts @@ -0,0 +1,395 @@ +import { Match, Template } from 'aws-cdk-lib/assertions'; +import * as iam from 'aws-cdk-lib/aws-iam'; +import * as core from 'aws-cdk-lib/core'; +import * as s3tables from '../lib'; +import * as perms from '../lib/permissions'; +import * as kms from 'aws-cdk-lib/aws-kms'; +import { singletonOrArr as stringIfSingle } from './test-utils'; + +const TABLE_BUCKET_CFN_RESOURCE = 'AWS::S3Tables::TableBucket'; +const TABLE_BUCKET_POLICY_CFN_RESOURCE = 'AWS::S3Tables::TableBucketPolicy'; +const KMS_KEY_CFN_RESOURCE = 'AWS::KMS::Key'; +const PRINCIPAL = 's3.amazonaws.com'; +const TABLE_BUCKET_ARN_SUB = { 'Fn::GetAtt': ['ExampleTableBucket9B5A2796', 'TableBucketARN'] }; +const TABLE_UUID = 'example-table-uuid'; +const EXISTING_ROLE_ARN = 'arn:aws:iam::123456789012:role/existing-role'; +const TABLE_BUCKET_NAME = 'example-table-bucket'; +const getResourcesWithTablesArn = (tableId: string) => [ + TABLE_BUCKET_ARN_SUB, + { 'Fn::Join': ['', [TABLE_BUCKET_ARN_SUB, `/table/${tableId}`]] }, +]; +const RESOURCES_WITH_TABLE_ARN = getResourcesWithTablesArn(TABLE_UUID); +const RESOURCES_WITH_WILDCARD = getResourcesWithTablesArn('*'); + +/* Allow quotes in the object keys used for CloudFormation template assertions */ +/* eslint-disable quote-props */ + +describe('TableBucket is created', () => { + let stack: core.Stack; + let tableBucket: s3tables.TableBucket; + let role: iam.Role; + let importedRole: iam.IRole; + let user: iam.User; + let userKey: kms.IKey; + + beforeEach(() => { + stack = new core.Stack(); + role = new iam.Role(stack, 'TestRole', { assumedBy: new iam.ServicePrincipal('sample') }); + user = new iam.User(stack, 'TestUser'); + importedRole = iam.Role.fromRoleArn(stack, 'ImportedRole', EXISTING_ROLE_ARN); + }); + + /** + * Templatizes grant tests across different test suites. + * + * @param withKMS whether to test for KMS policies + * @param keyName physical name of the KMS key to verify against + */ + const grantTests = ({ withKMS, keyName }: { withKMS : boolean; keyName?: string }) => { + enum GrantType { READ = 'read', WRITE = 'write', READ_WRITE = 'read & write' } + + const grantPermissions = (bucket: s3tables.TableBucket, grantType: GrantType, principal: iam.IGrantable, tableId: string) => { + switch (grantType) { + case GrantType.READ: + bucket.grantRead(principal, tableId); + return; + case GrantType.WRITE: + bucket.grantWrite(principal, tableId); + return; + case GrantType.READ_WRITE: + bucket.grantReadWrite(principal, tableId); + } + }; + + interface TestCase { + category: string; + grantType: GrantType; + actions: string | string[]; + keyActions: string | string[]; + } + + const testCases: TestCase[] = [ + { + category: 'grantRead', + grantType: GrantType.READ, + actions: stringIfSingle(perms.TABLE_BUCKET_READ_ACCESS), + keyActions: stringIfSingle(perms.KEY_READ_ACCESS), + }, + { + category: 'grantWrite', + grantType: GrantType.WRITE, + actions: stringIfSingle(perms.TABLE_BUCKET_WRITE_ACCESS), + keyActions: stringIfSingle(perms.KEY_WRITE_ACCESS), + }, + { + category: 'grantReadWrite', + grantType: GrantType.READ_WRITE, + actions: stringIfSingle(perms.TABLE_BUCKET_READ_WRITE_ACCESS), + keyActions: stringIfSingle(perms.KEY_READ_WRITE_ACCESS), + }, + ]; + + testCases.forEach(({ category, grantType, actions, keyActions }) => { + describe(category, () => { + const verifyKeyPolicies = () => { + if (withKMS) { + Template.fromStack(stack).hasResourceProperties(KMS_KEY_CFN_RESOURCE, { + 'KeyPolicy': { + 'Statement': Match.arrayWith([ + { + 'Action': keyActions, + 'Effect': 'Allow', + 'Principal': { + 'Service': PRINCIPAL, + }, + 'Resource': '*', + }, + ]), + }, + }); + } + }; + + it(`provides ${grantType} permissions to the bucket ${withKMS && 'and key'}`, () => { + grantPermissions(tableBucket, grantType, new iam.ServicePrincipal(PRINCIPAL), '*'); + Template.fromStack(stack).hasResourceProperties(TABLE_BUCKET_POLICY_CFN_RESOURCE, { + 'ResourcePolicy': { + 'Statement': [ + { + 'Action': actions, + 'Effect': 'Allow', + 'Principal': { + 'Service': PRINCIPAL, + }, + 'Resource': RESOURCES_WITH_WILDCARD, + }, + ], + }, + }); + verifyKeyPolicies(); + }); + + it(`provides ${grantType} permissions for a specific table ${withKMS && 'and key'}`, () => { + grantPermissions(tableBucket, grantType, new iam.ServicePrincipal(PRINCIPAL), TABLE_UUID); + Template.fromStack(stack).hasResourceProperties(TABLE_BUCKET_POLICY_CFN_RESOURCE, { + 'ResourcePolicy': { + 'Statement': [ + { + 'Action': actions, + 'Effect': 'Allow', + 'Principal': { + 'Service': PRINCIPAL, + }, + 'Resource': RESOURCES_WITH_TABLE_ARN, + }, + ], + }, + }); + verifyKeyPolicies(); + }); + + const expectedStatements : any = [{ + 'Action': actions, + 'Effect': 'Allow', + 'Resource': RESOURCES_WITH_TABLE_ARN, + }]; + if (withKMS) { + expectedStatements.push({ + 'Action': keyActions, + 'Effect': 'Allow', + 'Resource': { 'Fn::GetAtt': [keyName, 'Arn'] }, + }); + } + + it(`creates ${grantType} IAM policies for a role ${withKMS && 'and key'}`, () => { + grantPermissions(tableBucket, grantType, role, TABLE_UUID); + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { + 'PolicyDocument': { + 'Statement': expectedStatements, + 'Version': '2012-10-17', + }, + }); + Template.fromStack(stack).resourceCountIs(TABLE_BUCKET_POLICY_CFN_RESOURCE, 0); + }); + + it(`creates ${grantType} IAM policies for a user ${withKMS && 'and key'}`, () => { + grantPermissions(tableBucket, grantType, user, TABLE_UUID); + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { + 'PolicyDocument': { + 'Statement': expectedStatements, + 'Version': '2012-10-17', + }, + }); + Template.fromStack(stack).resourceCountIs(TABLE_BUCKET_POLICY_CFN_RESOURCE, 0); + }); + + it(`creates ${grantType} IAM policies for an imported role ${withKMS && 'and key'}`, () => { + grantPermissions(tableBucket, grantType, importedRole, TABLE_UUID); + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { + 'PolicyDocument': { + 'Statement': expectedStatements, + 'Version': '2012-10-17', + }, + }); + Template.fromStack(stack).hasResourceProperties(TABLE_BUCKET_POLICY_CFN_RESOURCE, { + 'ResourcePolicy': { + 'Statement': [ + { + 'Action': actions, + 'Effect': 'Allow', + 'Principal': { + 'AWS': EXISTING_ROLE_ARN, + }, + 'Resource': RESOURCES_WITH_TABLE_ARN, + }, + ], + }, + }); + }); + }); + }); + }; + + describe('with encryptionType undefined and encryptionKey provided', () => { + const keyName = 'ExampleKey469AF2A8'; + + beforeEach(() => { + userKey = new kms.Key(stack, 'ExampleKey', {}); + tableBucket = new s3tables.TableBucket(stack, 'ExampleTableBucket', { + account: '0123456789012', + region: 'us-west-2', + tableBucketName: TABLE_BUCKET_NAME, + encryptionKey: userKey, + removalPolicy: core.RemovalPolicy.DESTROY, + }); + }); + + it(`creates a ${TABLE_BUCKET_CFN_RESOURCE} resource`, () => { + Template.fromStack(stack).resourceCountIs(TABLE_BUCKET_CFN_RESOURCE, 1); + }); + + it('has encryption configuration', () => { + Template.fromStack(stack).hasResourceProperties(TABLE_BUCKET_CFN_RESOURCE, { + 'TableBucketName': TABLE_BUCKET_NAME, + 'EncryptionConfiguration': { + 'KMSKeyArn': { 'Fn::GetAtt': [keyName, 'Arn'] }, + 'SSEAlgorithm': 'aws:kms', + }, + }); + }); + + it('has removalPolicy set to Delete', () => { + Template.fromStack(stack).hasResource(TABLE_BUCKET_CFN_RESOURCE, { + 'DeletionPolicy': 'Delete', + }); + }); + + grantTests({ withKMS: true, keyName }); + }); + + describe('with encryptionType KMS and no encryptionKey provided', () => { + const keyName = 'ExampleTableBucketKey99B1A8DA'; + + beforeEach(() => { + tableBucket = new s3tables.TableBucket(stack, 'ExampleTableBucket', { + account: '0123456789012', + region: 'us-west-2', + tableBucketName: TABLE_BUCKET_NAME, + encryption: s3tables.TableBucketEncryption.KMS, + removalPolicy: core.RemovalPolicy.DESTROY, + }); + }); + + it(`creates a ${TABLE_BUCKET_CFN_RESOURCE} resource`, () => { + Template.fromStack(stack).resourceCountIs(TABLE_BUCKET_CFN_RESOURCE, 1); + }); + + it(`creates a ${KMS_KEY_CFN_RESOURCE} resource`, () => { + Template.fromStack(stack).resourceCountIs(KMS_KEY_CFN_RESOURCE, 1); + }); + + it('has encryption configuration', () => { + Template.fromStack(stack).hasResourceProperties(TABLE_BUCKET_CFN_RESOURCE, { + 'TableBucketName': TABLE_BUCKET_NAME, + 'EncryptionConfiguration': { + 'KMSKeyArn': { 'Fn::GetAtt': [keyName, 'Arn'] }, + 'SSEAlgorithm': 'aws:kms', + }, + }); + }); + + it('has removalPolicy set to Delete', () => { + Template.fromStack(stack).hasResource(TABLE_BUCKET_CFN_RESOURCE, { + 'DeletionPolicy': 'Delete', + }); + }); + + it('key allowlists S3Tables maintenance SP', () => { + Template.fromStack(stack).hasResourceProperties(KMS_KEY_CFN_RESOURCE, { + 'KeyPolicy': { + 'Statement': Match.arrayWith([ + { + 'Sid': 'AllowS3TablesMaintenanceAccess', + 'Action': [ + 'kms:GenerateDataKey', + 'kms:Decrypt', + ], + 'Effect': 'Allow', + 'Principal': { + 'Service': 'maintenance.s3tables.amazonaws.com', + }, + 'Resource': '*', + }, + ]), + }, + }); + }); + + grantTests({ withKMS: true, keyName }); + }); + + describe('with encryptionType KMS and user-defined encryptionKey', () => { + const keyName = 'ExampleKey469AF2A8'; + + beforeEach(() => { + userKey = new kms.Key(stack, 'ExampleKey', {}); + tableBucket = new s3tables.TableBucket(stack, 'ExampleTableBucket', { + account: '0123456789012', + region: 'us-west-2', + tableBucketName: TABLE_BUCKET_NAME, + encryption: s3tables.TableBucketEncryption.KMS, + encryptionKey: userKey, + removalPolicy: core.RemovalPolicy.DESTROY, + }); + }); + + it(`creates a ${TABLE_BUCKET_CFN_RESOURCE} resource`, () => { + Template.fromStack(stack).resourceCountIs(TABLE_BUCKET_CFN_RESOURCE, 1); + }); + + it('has encryption configuration', () => { + Template.fromStack(stack).hasResourceProperties(TABLE_BUCKET_CFN_RESOURCE, { + 'TableBucketName': TABLE_BUCKET_NAME, + 'EncryptionConfiguration': { + 'KMSKeyArn': { 'Fn::GetAtt': [keyName, 'Arn'] }, + 'SSEAlgorithm': 'aws:kms', + }, + }); + }); + + it('has removalPolicy set to Delete', () => { + Template.fromStack(stack).hasResource(TABLE_BUCKET_CFN_RESOURCE, { + 'DeletionPolicy': 'Delete', + }); + }); + + grantTests({ withKMS: true, keyName }); + }); + + describe('with encryptionType S3_MANAGED and no encryptionKey provided', () => { + beforeEach(() => { + tableBucket = new s3tables.TableBucket(stack, 'ExampleTableBucket', { + account: '0123456789012', + region: 'us-west-2', + tableBucketName: TABLE_BUCKET_NAME, + encryption: s3tables.TableBucketEncryption.S3_MANAGED, + removalPolicy: core.RemovalPolicy.DESTROY, + }); + }); + + it(`creates a ${TABLE_BUCKET_CFN_RESOURCE} resource`, () => { + Template.fromStack(stack).resourceCountIs(TABLE_BUCKET_CFN_RESOURCE, 1); + }); + + it('has encryption configuration', () => { + Template.fromStack(stack).hasResourceProperties(TABLE_BUCKET_CFN_RESOURCE, { + 'TableBucketName': TABLE_BUCKET_NAME, + 'EncryptionConfiguration': { + 'SSEAlgorithm': 'AES256', + }, + }); + }); + + it('has removalPolicy set to Delete', () => { + Template.fromStack(stack).hasResource(TABLE_BUCKET_CFN_RESOURCE, { + 'DeletionPolicy': 'Delete', + }); + }); + + grantTests({ withKMS: false }); + }); + + describe('with encryptionType S3_MANAGED and user-defined encryptionKey', () => { + it('throws a validation error in the constructor', () => { + userKey = new kms.Key(stack, 'ExampleKey', {}); + expect(() => new s3tables.TableBucket(stack, 'ExampleTableBucket', { + account: '0123456789012', + region: 'us-west-2', + tableBucketName: TABLE_BUCKET_NAME, + encryption: s3tables.TableBucketEncryption.S3_MANAGED, + encryptionKey: userKey, + removalPolicy: core.RemovalPolicy.DESTROY, + })).toThrow(); + }); + }); +}); 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..a9b32cda34b13 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 @@ -52,7 +52,7 @@ describe('TableBucket', () => { }); }); - describe('created with optional properties', () => { + describe('created with unreferenced file removal properties', () => { const TABLE_BUCKET_PROPS: s3tables.TableBucketProps = { account: '0123456789012', region: 'us-west-2', diff --git a/packages/@aws-cdk/aws-s3tables-alpha/test/test-utils.ts b/packages/@aws-cdk/aws-s3tables-alpha/test/test-utils.ts new file mode 100644 index 0000000000000..ddab6b520bb40 --- /dev/null +++ b/packages/@aws-cdk/aws-s3tables-alpha/test/test-utils.ts @@ -0,0 +1,6 @@ + +/** + * If the given array is 1-length, return the element in it. + * Useful for IAM policies/actions + */ +export const singletonOrArr = (array: string[]) => array.length === 1 ? array[0] : array; From a0501d825ce4d78f137fef7e11ca817ffeb7c3fb Mon Sep 17 00:00:00 2001 From: Soham Kulkarni Date: Mon, 28 Apr 2025 11:24:57 -0700 Subject: [PATCH 3/7] Add integration tests for TableBucket encryption --- .../KeyWithKMSEncryptionTypeTest.assets.json | 20 ++ ...KeyWithKMSEncryptionTypeTest.template.json | 91 +++++ .../OnlyEncryptionKeyTest.assets.json | 20 ++ .../OnlyEncryptionKeyTest.template.json | 91 +++++ .../OnlyKMSEncryptionTypeTest.assets.json | 20 ++ .../OnlyKMSEncryptionTypeTest.template.json | 105 ++++++ .../OnlyS3ManagedEncryptionTest.assets.json | 20 ++ .../OnlyS3ManagedEncryptionTest.template.json | 50 +++ ...efaultTestDeployAssert60E58AB6.assets.json | 20 ++ ...aultTestDeployAssert60E58AB6.template.json | 36 ++ .../cdk.out | 1 + .../integ.json | 15 + .../manifest.json | 339 ++++++++++++++++++ .../tree.json | 1 + .../integ.table-bucket-encryption.ts | 144 ++++++++ .../test/table-bucket-encryption.test.ts | 2 +- 16 files changed, 974 insertions(+), 1 deletion(-) create mode 100644 packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/KeyWithKMSEncryptionTypeTest.assets.json create mode 100644 packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/KeyWithKMSEncryptionTypeTest.template.json create mode 100644 packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/OnlyEncryptionKeyTest.assets.json create mode 100644 packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/OnlyEncryptionKeyTest.template.json create mode 100644 packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/OnlyKMSEncryptionTypeTest.assets.json create mode 100644 packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/OnlyKMSEncryptionTypeTest.template.json create mode 100644 packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/OnlyS3ManagedEncryptionTest.assets.json create mode 100644 packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/OnlyS3ManagedEncryptionTest.template.json create mode 100644 packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/TableBucketWithGrantIntegTestDefaultTestDeployAssert60E58AB6.assets.json create mode 100644 packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/TableBucketWithGrantIntegTestDefaultTestDeployAssert60E58AB6.template.json create mode 100644 packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/cdk.out create mode 100644 packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/integ.json create mode 100644 packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/manifest.json create mode 100644 packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/tree.json create mode 100644 packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.ts diff --git a/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/KeyWithKMSEncryptionTypeTest.assets.json b/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/KeyWithKMSEncryptionTypeTest.assets.json new file mode 100644 index 0000000000000..e1a9166fc9575 --- /dev/null +++ b/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/KeyWithKMSEncryptionTypeTest.assets.json @@ -0,0 +1,20 @@ +{ + "version": "41.0.0", + "files": { + "0754b8b9a278cd7d48040522b6423a2461ae6261c2d9a5f93f5598f74198c824": { + "displayName": "KeyWithKMSEncryptionTypeTest Template", + "source": { + "path": "KeyWithKMSEncryptionTypeTest.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "0754b8b9a278cd7d48040522b6423a2461ae6261c2d9a5f93f5598f74198c824.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-encryption.js.snapshot/KeyWithKMSEncryptionTypeTest.template.json b/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/KeyWithKMSEncryptionTypeTest.template.json new file mode 100644 index 0000000000000..aaea2e76a0d4e --- /dev/null +++ b/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/KeyWithKMSEncryptionTypeTest.template.json @@ -0,0 +1,91 @@ +{ + "Resources": { + "Key961B73FD": { + "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": "*" + } + ], + "Version": "2012-10-17" + } + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, + "KeyWithKMSEncryptionTypeTestF4BB0051": { + "Type": "AWS::S3Tables::TableBucket", + "Properties": { + "EncryptionConfiguration": { + "KMSKeyArn": { + "Fn::GetAtt": [ + "Key961B73FD", + "Arn" + ] + }, + "SSEAlgorithm": "aws:kms" + }, + "TableBucketName": "integ-tb-key-with-type", + "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-encryption.js.snapshot/OnlyEncryptionKeyTest.assets.json b/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/OnlyEncryptionKeyTest.assets.json new file mode 100644 index 0000000000000..1d6a9e370a5db --- /dev/null +++ b/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/OnlyEncryptionKeyTest.assets.json @@ -0,0 +1,20 @@ +{ + "version": "41.0.0", + "files": { + "7fcc56d2d76068041a26a7eb195a7123a2080f59e47e3064be079312f36be91d": { + "displayName": "OnlyEncryptionKeyTest Template", + "source": { + "path": "OnlyEncryptionKeyTest.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "7fcc56d2d76068041a26a7eb195a7123a2080f59e47e3064be079312f36be91d.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-encryption.js.snapshot/OnlyEncryptionKeyTest.template.json b/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/OnlyEncryptionKeyTest.template.json new file mode 100644 index 0000000000000..20f4c796b0a84 --- /dev/null +++ b/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/OnlyEncryptionKeyTest.template.json @@ -0,0 +1,91 @@ +{ + "Resources": { + "Key961B73FD": { + "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": "*" + } + ], + "Version": "2012-10-17" + } + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, + "OnlyEncryptionKeyTest2F21C8CC": { + "Type": "AWS::S3Tables::TableBucket", + "Properties": { + "EncryptionConfiguration": { + "KMSKeyArn": { + "Fn::GetAtt": [ + "Key961B73FD", + "Arn" + ] + }, + "SSEAlgorithm": "aws:kms" + }, + "TableBucketName": "integ-tb-key-only", + "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-encryption.js.snapshot/OnlyKMSEncryptionTypeTest.assets.json b/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/OnlyKMSEncryptionTypeTest.assets.json new file mode 100644 index 0000000000000..e20052571272a --- /dev/null +++ b/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/OnlyKMSEncryptionTypeTest.assets.json @@ -0,0 +1,20 @@ +{ + "version": "41.0.0", + "files": { + "610424a8b18bb9d397c2cc1bc2b0bfd9192556a38b603fc70f7140e91fd9a224": { + "displayName": "OnlyKMSEncryptionTypeTest Template", + "source": { + "path": "OnlyKMSEncryptionTypeTest.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "610424a8b18bb9d397c2cc1bc2b0bfd9192556a38b603fc70f7140e91fd9a224.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-encryption.js.snapshot/OnlyKMSEncryptionTypeTest.template.json b/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/OnlyKMSEncryptionTypeTest.template.json new file mode 100644 index 0000000000000..4e4f58075acf6 --- /dev/null +++ b/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/OnlyKMSEncryptionTypeTest.template.json @@ -0,0 +1,105 @@ +{ + "Resources": { + "OnlyKMSEncryptionTypeTestKey190897FD": { + "Type": "AWS::KMS::Key", + "Properties": { + "Description": "Created by OnlyKMSEncryptionTypeTest/OnlyKMSEncryptionTypeTest", + "EnableKeyRotation": true, + "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" + ], + "Effect": "Allow", + "Principal": { + "Service": "maintenance.s3tables.amazonaws.com" + }, + "Resource": "*", + "Sid": "AllowS3TablesMaintenanceAccess" + } + ], + "Version": "2012-10-17" + } + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, + "OnlyKMSEncryptionTypeTestB8E6F4B3": { + "Type": "AWS::S3Tables::TableBucket", + "Properties": { + "EncryptionConfiguration": { + "KMSKeyArn": { + "Fn::GetAtt": [ + "OnlyKMSEncryptionTypeTestKey190897FD", + "Arn" + ] + }, + "SSEAlgorithm": "aws:kms" + }, + "TableBucketName": "integ-tb-kms-encryption-type-only", + "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-encryption.js.snapshot/OnlyS3ManagedEncryptionTest.assets.json b/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/OnlyS3ManagedEncryptionTest.assets.json new file mode 100644 index 0000000000000..62696d5a4b0c9 --- /dev/null +++ b/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/OnlyS3ManagedEncryptionTest.assets.json @@ -0,0 +1,20 @@ +{ + "version": "41.0.0", + "files": { + "6aa33954737a1f4a9379d1492c7444f67eb487ec6f415fc2be992977ed509d9a": { + "displayName": "OnlyS3ManagedEncryptionTest Template", + "source": { + "path": "OnlyS3ManagedEncryptionTest.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "6aa33954737a1f4a9379d1492c7444f67eb487ec6f415fc2be992977ed509d9a.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-encryption.js.snapshot/OnlyS3ManagedEncryptionTest.template.json b/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/OnlyS3ManagedEncryptionTest.template.json new file mode 100644 index 0000000000000..e2e61dcce0e5a --- /dev/null +++ b/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/OnlyS3ManagedEncryptionTest.template.json @@ -0,0 +1,50 @@ +{ + "Resources": { + "OnlyS3ManagedEncryptionTest146B7FA9": { + "Type": "AWS::S3Tables::TableBucket", + "Properties": { + "EncryptionConfiguration": { + "SSEAlgorithm": "AES256" + }, + "TableBucketName": "integ-tb-s3-managed-encryption-type-only", + "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-encryption.js.snapshot/TableBucketWithGrantIntegTestDefaultTestDeployAssert60E58AB6.assets.json b/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/TableBucketWithGrantIntegTestDefaultTestDeployAssert60E58AB6.assets.json new file mode 100644 index 0000000000000..c3d07bfb04855 --- /dev/null +++ b/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/TableBucketWithGrantIntegTestDefaultTestDeployAssert60E58AB6.assets.json @@ -0,0 +1,20 @@ +{ + "version": "41.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "displayName": "TableBucketWithGrantIntegTestDefaultTestDeployAssert60E58AB6 Template", + "source": { + "path": "TableBucketWithGrantIntegTestDefaultTestDeployAssert60E58AB6.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-encryption.js.snapshot/TableBucketWithGrantIntegTestDefaultTestDeployAssert60E58AB6.template.json b/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/TableBucketWithGrantIntegTestDefaultTestDeployAssert60E58AB6.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/TableBucketWithGrantIntegTestDefaultTestDeployAssert60E58AB6.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-encryption.js.snapshot/cdk.out b/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.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-encryption.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-encryption.js.snapshot/integ.json b/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/integ.json new file mode 100644 index 0000000000000..982b13c196e18 --- /dev/null +++ b/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/integ.json @@ -0,0 +1,15 @@ +{ + "version": "41.0.0", + "testCases": { + "TableBucketWithGrantIntegTest/DefaultTest": { + "stacks": [ + "OnlyEncryptionKeyTest", + "OnlyKMSEncryptionTypeTest", + "KeyWithKMSEncryptionTypeTest", + "OnlyS3ManagedEncryptionTest" + ], + "assertionStack": "TableBucketWithGrantIntegTest/DefaultTest/DeployAssert", + "assertionStackName": "TableBucketWithGrantIntegTestDefaultTestDeployAssert60E58AB6" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/manifest.json b/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/manifest.json new file mode 100644 index 0000000000000..60e50c64b831e --- /dev/null +++ b/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/manifest.json @@ -0,0 +1,339 @@ +{ + "version": "42.0.0", + "artifacts": { + "OnlyEncryptionKeyTest.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "OnlyEncryptionKeyTest.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "OnlyEncryptionKeyTest": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "OnlyEncryptionKeyTest.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}/7fcc56d2d76068041a26a7eb195a7123a2080f59e47e3064be079312f36be91d.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "OnlyEncryptionKeyTest.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": [ + "OnlyEncryptionKeyTest.assets" + ], + "metadata": { + "/OnlyEncryptionKeyTest/Key": [ + { + "type": "aws:cdk:analytics:construct", + "data": "*" + } + ], + "/OnlyEncryptionKeyTest/Key/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "Key961B73FD" + } + ], + "/OnlyEncryptionKeyTest/OnlyEncryptionKeyTest": [ + { + "type": "aws:cdk:analytics:construct", + "data": "*" + } + ], + "/OnlyEncryptionKeyTest/OnlyEncryptionKeyTest/OnlyEncryptionKeyTest": [ + { + "type": "aws:cdk:logicalId", + "data": "OnlyEncryptionKeyTest2F21C8CC" + } + ], + "/OnlyEncryptionKeyTest/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/OnlyEncryptionKeyTest/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "OnlyEncryptionKeyTest" + }, + "OnlyKMSEncryptionTypeTest.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "OnlyKMSEncryptionTypeTest.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "OnlyKMSEncryptionTypeTest": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "OnlyKMSEncryptionTypeTest.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}/610424a8b18bb9d397c2cc1bc2b0bfd9192556a38b603fc70f7140e91fd9a224.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "OnlyKMSEncryptionTypeTest.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": [ + "OnlyKMSEncryptionTypeTest.assets" + ], + "metadata": { + "/OnlyKMSEncryptionTypeTest/OnlyKMSEncryptionTypeTest": [ + { + "type": "aws:cdk:analytics:construct", + "data": "*" + } + ], + "/OnlyKMSEncryptionTypeTest/OnlyKMSEncryptionTypeTest/Key": [ + { + "type": "aws:cdk:analytics:construct", + "data": { + "description": "*", + "enableKeyRotation": true + } + } + ], + "/OnlyKMSEncryptionTypeTest/OnlyKMSEncryptionTypeTest/Key/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "OnlyKMSEncryptionTypeTestKey190897FD" + } + ], + "/OnlyKMSEncryptionTypeTest/OnlyKMSEncryptionTypeTest/OnlyKMSEncryptionTypeTest": [ + { + "type": "aws:cdk:logicalId", + "data": "OnlyKMSEncryptionTypeTestB8E6F4B3" + } + ], + "/OnlyKMSEncryptionTypeTest/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/OnlyKMSEncryptionTypeTest/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "OnlyKMSEncryptionTypeTest" + }, + "KeyWithKMSEncryptionTypeTest.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "KeyWithKMSEncryptionTypeTest.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "KeyWithKMSEncryptionTypeTest": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "KeyWithKMSEncryptionTypeTest.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}/0754b8b9a278cd7d48040522b6423a2461ae6261c2d9a5f93f5598f74198c824.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "KeyWithKMSEncryptionTypeTest.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": [ + "KeyWithKMSEncryptionTypeTest.assets" + ], + "metadata": { + "/KeyWithKMSEncryptionTypeTest/Key": [ + { + "type": "aws:cdk:analytics:construct", + "data": "*" + } + ], + "/KeyWithKMSEncryptionTypeTest/Key/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "Key961B73FD" + } + ], + "/KeyWithKMSEncryptionTypeTest/KeyWithKMSEncryptionTypeTest": [ + { + "type": "aws:cdk:analytics:construct", + "data": "*" + } + ], + "/KeyWithKMSEncryptionTypeTest/KeyWithKMSEncryptionTypeTest/KeyWithKMSEncryptionTypeTest": [ + { + "type": "aws:cdk:logicalId", + "data": "KeyWithKMSEncryptionTypeTestF4BB0051" + } + ], + "/KeyWithKMSEncryptionTypeTest/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/KeyWithKMSEncryptionTypeTest/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "KeyWithKMSEncryptionTypeTest" + }, + "OnlyS3ManagedEncryptionTest.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "OnlyS3ManagedEncryptionTest.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "OnlyS3ManagedEncryptionTest": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "OnlyS3ManagedEncryptionTest.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}/6aa33954737a1f4a9379d1492c7444f67eb487ec6f415fc2be992977ed509d9a.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "OnlyS3ManagedEncryptionTest.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": [ + "OnlyS3ManagedEncryptionTest.assets" + ], + "metadata": { + "/OnlyS3ManagedEncryptionTest/OnlyS3ManagedEncryptionTest": [ + { + "type": "aws:cdk:analytics:construct", + "data": "*" + } + ], + "/OnlyS3ManagedEncryptionTest/OnlyS3ManagedEncryptionTest/OnlyS3ManagedEncryptionTest": [ + { + "type": "aws:cdk:logicalId", + "data": "OnlyS3ManagedEncryptionTest146B7FA9" + } + ], + "/OnlyS3ManagedEncryptionTest/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/OnlyS3ManagedEncryptionTest/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "OnlyS3ManagedEncryptionTest" + }, + "TableBucketWithGrantIntegTestDefaultTestDeployAssert60E58AB6.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "TableBucketWithGrantIntegTestDefaultTestDeployAssert60E58AB6.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "TableBucketWithGrantIntegTestDefaultTestDeployAssert60E58AB6": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "TableBucketWithGrantIntegTestDefaultTestDeployAssert60E58AB6.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": [ + "TableBucketWithGrantIntegTestDefaultTestDeployAssert60E58AB6.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": [ + "TableBucketWithGrantIntegTestDefaultTestDeployAssert60E58AB6.assets" + ], + "metadata": { + "/TableBucketWithGrantIntegTest/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/TableBucketWithGrantIntegTest/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "TableBucketWithGrantIntegTest/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-encryption.js.snapshot/tree.json b/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/tree.json new file mode 100644 index 0000000000000..4fc2711f9b52c --- /dev/null +++ b/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/tree.json @@ -0,0 +1 @@ +{"version":"tree-0.1","tree":{"id":"App","path":"","children":{"OnlyEncryptionKeyTest":{"id":"OnlyEncryptionKeyTest","path":"OnlyEncryptionKeyTest","children":{"Key":{"id":"Key","path":"OnlyEncryptionKeyTest/Key","children":{"Resource":{"id":"Resource","path":"OnlyEncryptionKeyTest/Key/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":"*"}],"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":["*"]}},"OnlyEncryptionKeyTest":{"id":"OnlyEncryptionKeyTest","path":"OnlyEncryptionKeyTest/OnlyEncryptionKeyTest","children":{"OnlyEncryptionKeyTest":{"id":"OnlyEncryptionKeyTest","path":"OnlyEncryptionKeyTest/OnlyEncryptionKeyTest/OnlyEncryptionKeyTest","attributes":{"aws:cdk:cloudformation:type":"AWS::S3Tables::TableBucket","aws:cdk:cloudformation:props":{"encryptionConfiguration":{"kmsKeyArn":{"Fn::GetAtt":["Key961B73FD","Arn"]},"sseAlgorithm":"aws:kms"},"tableBucketName":"integ-tb-key-only","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":"OnlyEncryptionKeyTest/BootstrapVersion","constructInfo":{"fqn":"aws-cdk-lib.CfnParameter","version":"0.0.0"}},"CheckBootstrapVersion":{"id":"CheckBootstrapVersion","path":"OnlyEncryptionKeyTest/CheckBootstrapVersion","constructInfo":{"fqn":"aws-cdk-lib.CfnRule","version":"0.0.0"}}},"constructInfo":{"fqn":"aws-cdk-lib.Stack","version":"0.0.0"}},"OnlyKMSEncryptionTypeTest":{"id":"OnlyKMSEncryptionTypeTest","path":"OnlyKMSEncryptionTypeTest","children":{"OnlyKMSEncryptionTypeTest":{"id":"OnlyKMSEncryptionTypeTest","path":"OnlyKMSEncryptionTypeTest/OnlyKMSEncryptionTypeTest","children":{"Key":{"id":"Key","path":"OnlyKMSEncryptionTypeTest/OnlyKMSEncryptionTypeTest/Key","children":{"Resource":{"id":"Resource","path":"OnlyKMSEncryptionTypeTest/OnlyKMSEncryptionTypeTest/Key/Resource","attributes":{"aws:cdk:cloudformation:type":"AWS::KMS::Key","aws:cdk:cloudformation:props":{"description":"Created by OnlyKMSEncryptionTypeTest/OnlyKMSEncryptionTypeTest","enableKeyRotation":true,"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"],"Effect":"Allow","Principal":{"Service":"maintenance.s3tables.amazonaws.com"},"Resource":"*","Sid":"AllowS3TablesMaintenanceAccess"}],"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":[{"description":"*","enableKeyRotation":true}]}},"OnlyKMSEncryptionTypeTest":{"id":"OnlyKMSEncryptionTypeTest","path":"OnlyKMSEncryptionTypeTest/OnlyKMSEncryptionTypeTest/OnlyKMSEncryptionTypeTest","attributes":{"aws:cdk:cloudformation:type":"AWS::S3Tables::TableBucket","aws:cdk:cloudformation:props":{"encryptionConfiguration":{"kmsKeyArn":{"Fn::GetAtt":["OnlyKMSEncryptionTypeTestKey190897FD","Arn"]},"sseAlgorithm":"aws:kms"},"tableBucketName":"integ-tb-kms-encryption-type-only","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":"OnlyKMSEncryptionTypeTest/BootstrapVersion","constructInfo":{"fqn":"aws-cdk-lib.CfnParameter","version":"0.0.0"}},"CheckBootstrapVersion":{"id":"CheckBootstrapVersion","path":"OnlyKMSEncryptionTypeTest/CheckBootstrapVersion","constructInfo":{"fqn":"aws-cdk-lib.CfnRule","version":"0.0.0"}}},"constructInfo":{"fqn":"aws-cdk-lib.Stack","version":"0.0.0"}},"KeyWithKMSEncryptionTypeTest":{"id":"KeyWithKMSEncryptionTypeTest","path":"KeyWithKMSEncryptionTypeTest","children":{"Key":{"id":"Key","path":"KeyWithKMSEncryptionTypeTest/Key","children":{"Resource":{"id":"Resource","path":"KeyWithKMSEncryptionTypeTest/Key/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":"*"}],"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":["*"]}},"KeyWithKMSEncryptionTypeTest":{"id":"KeyWithKMSEncryptionTypeTest","path":"KeyWithKMSEncryptionTypeTest/KeyWithKMSEncryptionTypeTest","children":{"KeyWithKMSEncryptionTypeTest":{"id":"KeyWithKMSEncryptionTypeTest","path":"KeyWithKMSEncryptionTypeTest/KeyWithKMSEncryptionTypeTest/KeyWithKMSEncryptionTypeTest","attributes":{"aws:cdk:cloudformation:type":"AWS::S3Tables::TableBucket","aws:cdk:cloudformation:props":{"encryptionConfiguration":{"kmsKeyArn":{"Fn::GetAtt":["Key961B73FD","Arn"]},"sseAlgorithm":"aws:kms"},"tableBucketName":"integ-tb-key-with-type","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":"KeyWithKMSEncryptionTypeTest/BootstrapVersion","constructInfo":{"fqn":"aws-cdk-lib.CfnParameter","version":"0.0.0"}},"CheckBootstrapVersion":{"id":"CheckBootstrapVersion","path":"KeyWithKMSEncryptionTypeTest/CheckBootstrapVersion","constructInfo":{"fqn":"aws-cdk-lib.CfnRule","version":"0.0.0"}}},"constructInfo":{"fqn":"aws-cdk-lib.Stack","version":"0.0.0"}},"OnlyS3ManagedEncryptionTest":{"id":"OnlyS3ManagedEncryptionTest","path":"OnlyS3ManagedEncryptionTest","children":{"OnlyS3ManagedEncryptionTest":{"id":"OnlyS3ManagedEncryptionTest","path":"OnlyS3ManagedEncryptionTest/OnlyS3ManagedEncryptionTest","children":{"OnlyS3ManagedEncryptionTest":{"id":"OnlyS3ManagedEncryptionTest","path":"OnlyS3ManagedEncryptionTest/OnlyS3ManagedEncryptionTest/OnlyS3ManagedEncryptionTest","attributes":{"aws:cdk:cloudformation:type":"AWS::S3Tables::TableBucket","aws:cdk:cloudformation:props":{"encryptionConfiguration":{"sseAlgorithm":"AES256"},"tableBucketName":"integ-tb-s3-managed-encryption-type-only","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":"OnlyS3ManagedEncryptionTest/BootstrapVersion","constructInfo":{"fqn":"aws-cdk-lib.CfnParameter","version":"0.0.0"}},"CheckBootstrapVersion":{"id":"CheckBootstrapVersion","path":"OnlyS3ManagedEncryptionTest/CheckBootstrapVersion","constructInfo":{"fqn":"aws-cdk-lib.CfnRule","version":"0.0.0"}}},"constructInfo":{"fqn":"aws-cdk-lib.Stack","version":"0.0.0"}},"TableBucketWithGrantIntegTest":{"id":"TableBucketWithGrantIntegTest","path":"TableBucketWithGrantIntegTest","children":{"DefaultTest":{"id":"DefaultTest","path":"TableBucketWithGrantIntegTest/DefaultTest","children":{"Default":{"id":"Default","path":"TableBucketWithGrantIntegTest/DefaultTest/Default","constructInfo":{"fqn":"constructs.Construct","version":"10.4.2"}},"DeployAssert":{"id":"DeployAssert","path":"TableBucketWithGrantIntegTest/DefaultTest/DeployAssert","children":{"BootstrapVersion":{"id":"BootstrapVersion","path":"TableBucketWithGrantIntegTest/DefaultTest/DeployAssert/BootstrapVersion","constructInfo":{"fqn":"aws-cdk-lib.CfnParameter","version":"0.0.0"}},"CheckBootstrapVersion":{"id":"CheckBootstrapVersion","path":"TableBucketWithGrantIntegTest/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-encryption.ts b/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.ts new file mode 100644 index 0000000000000..f99cbe8d49561 --- /dev/null +++ b/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.ts @@ -0,0 +1,144 @@ +import * as core from 'aws-cdk-lib/core'; +import * as s3tables from '../../lib'; +import { Construct } from 'constructs'; +import { ExpectedResult, IntegTest, Match } from '@aws-cdk/integ-tests-alpha'; +import * as kms from 'aws-cdk-lib/aws-kms'; + +/** + * Test cases: + * + * | props.encryption | props.encryptionKey | bucketEncryption (expected) | encryptionKey (expected) | + * |------------------|---------------------|---------------------------------|-------------------------------| + * | undefined | k | aws:kms | k | + * | KMS | undefined | aws:kms | new key (allow maintenance SP)| + * | KMS | k | aws:kms | k | + * | S3_MANAGED | undefined | AES256 | undefined | + */ + +abstract class EncryptionTestBase extends core.Stack { + public abstract validateAssertions(integ: IntegTest): void; +} + +class OnlyEncryptionKeyTest extends EncryptionTestBase { + public readonly tableBucket: s3tables.TableBucket; + public readonly key: kms.IKey; + + constructor(scope: Construct, id: string, props?: core.StackProps) { + super(scope, id, props); + this.key = new kms.Key(this, 'Key', {}); + this.tableBucket = new s3tables.TableBucket(this, id, { + tableBucketName: 'integ-tb-key-only', + encryptionKey: this.key, + removalPolicy: core.RemovalPolicy.DESTROY, + }); + } + + public validateAssertions(integ: IntegTest) { + const encryptionConfig = integ.assertions.awsApiCall('@aws-sdk/client-s3tables', 'GetTableBucketEncryptionCommand', { + tableBucketARN: this.tableBucket.tableBucketArn, + }); + + encryptionConfig.expect(ExpectedResult.objectLike({ + encryptionConfiguration: { + sseAlgorithm: 'aws:kms', + kmsKeyArn: this.key.keyArn, + }, + })); + } +} + +class OnlyKMSEncryptionTypeTest extends core.Stack { + public readonly tableBucket: s3tables.TableBucket; + + constructor(scope: Construct, id: string, props?: core.StackProps) { + super(scope, id, props); + this.tableBucket = new s3tables.TableBucket(this, id, { + tableBucketName: 'integ-tb-kms-encryption-type-only', + encryption: s3tables.TableBucketEncryption.KMS, + removalPolicy: core.RemovalPolicy.DESTROY, + }); + } + + public validateAssertions(integ: IntegTest) { + const encryptionConfig = integ.assertions.awsApiCall('@aws-sdk/client-s3tables', 'GetTableBucketEncryptionCommand', { + tableBucketARN: this.tableBucket.tableBucketArn, + }); + + encryptionConfig.expect(ExpectedResult.objectLike({ + encryptionConfiguration: { + sseAlgorithm: 'aws:kms', + kmsKeyArn: Match.stringLikeRegexp('arn:aws:kms:.*:key/[\w-]+'), + }, + })); + } +} +class KeyWithKMSEncryptionTypeTest extends core.Stack { + public readonly tableBucket: s3tables.TableBucket; + public readonly key: kms.IKey; + + constructor(scope: Construct, id: string, props?: core.StackProps) { + super(scope, id, props); + this.key = new kms.Key(this, 'Key', {}); + this.tableBucket = new s3tables.TableBucket(this, id, { + tableBucketName: 'integ-tb-key-with-type', + account: props?.env?.account, + region: props?.env?.region, + encryption: s3tables.TableBucketEncryption.KMS, + encryptionKey: this.key, + removalPolicy: core.RemovalPolicy.DESTROY, + }); + } + + public validateAssertions(integ: IntegTest) { + const encryptionConfig = integ.assertions.awsApiCall('@aws-sdk/client-s3tables', 'GetTableBucketEncryptionCommand', { + tableBucketARN: this.tableBucket.tableBucketArn, + }); + + encryptionConfig.expect(ExpectedResult.objectLike({ + encryptionConfiguration: { + sseAlgorithm: 'aws:kms', + kmsKeyArn: this.key.keyArn, + }, + })); + } +} +class OnlyS3ManagedEncryptionTest extends core.Stack { + public readonly tableBucket: s3tables.TableBucket; + + constructor(scope: Construct, id: string, props?: core.StackProps) { + super(scope, id, props); + this.tableBucket = new s3tables.TableBucket(this, id, { + tableBucketName: 'integ-tb-s3-managed-encryption-type-only', + encryption: s3tables.TableBucketEncryption.S3_MANAGED, + removalPolicy: core.RemovalPolicy.DESTROY, + }); + } + + public validateAssertions(integ: IntegTest) { + const encryptionConfig = integ.assertions.awsApiCall('@aws-sdk/client-s3tables', 'GetTableBucketEncryptionCommand', { + tableBucketARN: this.tableBucket.tableBucketArn, + }); + + encryptionConfig.expect(ExpectedResult.objectLike({ + encryptionConfiguration: { + sseAlgorithm: 'AES256', + }, + })); + } +} + +const app = new core.App(); + +const testCases = [ + new OnlyEncryptionKeyTest(app, 'OnlyEncryptionKeyTest', {}), + new OnlyKMSEncryptionTypeTest(app, 'OnlyKMSEncryptionTypeTest', {}), + new KeyWithKMSEncryptionTypeTest(app, 'KeyWithKMSEncryptionTypeTest', {}), + new OnlyS3ManagedEncryptionTest(app, 'OnlyS3ManagedEncryptionTest', {}), +]; + +new IntegTest(app, 'TableBucketEncryptionIntegTest', { testCases }); + +// TODO: Uncomment to add assertions once SDK is updated to include GetTableBucketEncryption API +// testCases.forEach(testCase => testCase.validateAssertions(integ)); + +app.synth(); diff --git a/packages/@aws-cdk/aws-s3tables-alpha/test/table-bucket-encryption.test.ts b/packages/@aws-cdk/aws-s3tables-alpha/test/table-bucket-encryption.test.ts index 68eb84d845c36..10378a3d99fe4 100644 --- a/packages/@aws-cdk/aws-s3tables-alpha/test/table-bucket-encryption.test.ts +++ b/packages/@aws-cdk/aws-s3tables-alpha/test/table-bucket-encryption.test.ts @@ -24,7 +24,7 @@ const RESOURCES_WITH_WILDCARD = getResourcesWithTablesArn('*'); /* Allow quotes in the object keys used for CloudFormation template assertions */ /* eslint-disable quote-props */ -describe('TableBucket is created', () => { +describe('TableBucket with encryption', () => { let stack: core.Stack; let tableBucket: s3tables.TableBucket; let role: iam.Role; From 999f0372fd720224f8eb2f3fbdb0e00a75fd17d2 Mon Sep 17 00:00:00 2001 From: Soham Kulkarni Date: Mon, 28 Apr 2025 11:38:27 -0700 Subject: [PATCH 4/7] Update documentation for KMS support --- .../@aws-cdk/aws-s3tables-alpha/README.md | 34 +++++++++++++++++++ .../rosetta/default.ts-fixture | 3 +- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-s3tables-alpha/README.md b/packages/@aws-cdk/aws-s3tables-alpha/README.md index 2fb4986e9b3cf..cbd6e22cfd122 100644 --- a/packages/@aws-cdk/aws-s3tables-alpha/README.md +++ b/packages/@aws-cdk/aws-s3tables-alpha/README.md @@ -47,9 +47,11 @@ Learn more about table buckets maintenance operations and default behavior from // Grant the principal read permissions to the bucket and all tables within const accountId = '123456789012' tableBucket.grantRead(new iam.AccountPrincipal(accountId), '*'); + // Grant the role write permissions to the bucket and all tables within const role = new iam.Role(stack, 'MyRole', { assumedBy: new iam.ServicePrincipal('sample') }); tableBucket.grantWrite(role, '*'); + // Grant the user read and write permissions to the bucket and all tables within tableBucket.grantReadWrite(new iam.User(stack, 'MyUser'), '*'); @@ -68,6 +70,38 @@ const permissions = new iam.PolicyStatement({ tableBucket.addToResourcePolicy(permissions); ``` +### Controlling Table Bucket Encryption Settings + +S3 TableBuckets have SSE (server-side encryption) enabled by default with S3 managed keys. +You can also bring your own KMS key for KMS-SSE or have S3 create a KMS key for you. + +If a bucket is encrypted with KMS, grant functions on the bucket will also grant access +to the TableBucket's associated KMS key + +```ts +// Provide a user defined KMS Key: +const key = new kms.Key(scope, 'UserKey', {}); +const encryptedBucket = new TableBucket(scope, 'EncryptedTableBucket', { + tableBucketName: 'table-bucket-1', + encryption: TableBucketEncryption.KMS, + encryptionKey: key, +}); +// This account principal will also receive kms:Decrypt access to the KMS key +encryptedBucket.grantRead(new iam.AccountPrincipal('123456789012'), '*'); + +// If no key is provided, one will be created automatically +const encryptedBucketAuto = new TableBucket(scope, 'EncryptedTableBucketAuto', { + tableBucketName: 'table-bucket-2', + encryption: TableBucketEncryption.KMS, +}); + +// Use S3 managed server side encryption (default) +const encryptedBucketDefault = new TableBucket(scope, 'EncryptedTableBucketDefault', { + tableBucketName: 'table-bucket-3', + encryption: TableBucketEncryption.S3_MANAGED, +}); +``` + ## Coming Soon L2 Construct support for: 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..e71a07bb0fc21 100644 --- a/packages/@aws-cdk/aws-s3tables-alpha/rosetta/default.ts-fixture +++ b/packages/@aws-cdk/aws-s3tables-alpha/rosetta/default.ts-fixture @@ -1,7 +1,8 @@ import { Construct } from 'constructs'; import { Stack } from 'aws-cdk-lib'; -import { TableBucket, UnreferencedFileRemovalStatus } from '@aws-cdk/aws-s3tables-alpha'; +import { TableBucket, UnreferencedFileRemovalStatus, TableBucketEncryption } 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) { From 5077264fffaa96fec3a346ea7bccb519a992fd1c Mon Sep 17 00:00:00 2001 From: Soham Kulkarni Date: Mon, 28 Apr 2025 12:17:22 -0700 Subject: [PATCH 5/7] Fix key perms and parseEncryption error message --- packages/@aws-cdk/aws-s3tables-alpha/lib/table-bucket.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 c793ce7f56c46..7677d8cbdd489 100644 --- a/packages/@aws-cdk/aws-s3tables-alpha/lib/table-bucket.ts +++ b/packages/@aws-cdk/aws-s3tables-alpha/lib/table-bucket.ts @@ -231,7 +231,7 @@ abstract class TableBucketBase extends Resource implements ITableBucket { return this.grant( identity, perms.TABLE_BUCKET_WRITE_ACCESS, - perms.KEY_WRITE_ACCESS, + perms.KEY_READ_WRITE_ACCESS, this.tableBucketArn, this.getTableArn(tableId), ); @@ -426,7 +426,7 @@ export class TableBucket extends TableBucketBase { public readonly tableBucketPolicy?: TableBucketPolicy; public readonly region = region; public readonly account = account; - public readonly encryptionKey?: kms.IKey | undefined; + public readonly encryptionKey?: kms.IKey = attrs.encryptionKey; protected autoCreatePolicy: boolean = false; /** @@ -662,7 +662,7 @@ export class TableBucket extends TableBucketBase { throw new UnscopedValidationError('Expected encryption = `KMS` with user provided encryption key'); } } - throw new UnscopedValidationError('Expected encryption = `KMS` with user provided encryption key'); + throw new UnscopedValidationError(`Unknown encryption configuration detected: ${props.encryption} with key ${props.encryptionKey}`); } /** From 21733dba548c391d29b0ddd66ab71ac757ba1729 Mon Sep 17 00:00:00 2001 From: Soham Kulkarni Date: Wed, 7 May 2025 15:41:34 -0700 Subject: [PATCH 6/7] Add additional information about auto generated KMS keys --- .../@aws-cdk/aws-s3tables-alpha/README.md | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/packages/@aws-cdk/aws-s3tables-alpha/README.md b/packages/@aws-cdk/aws-s3tables-alpha/README.md index cbd6e22cfd122..6808065df7d2c 100644 --- a/packages/@aws-cdk/aws-s3tables-alpha/README.md +++ b/packages/@aws-cdk/aws-s3tables-alpha/README.md @@ -72,11 +72,11 @@ tableBucket.addToResourcePolicy(permissions); ### Controlling Table Bucket Encryption Settings -S3 TableBuckets have SSE (server-side encryption) enabled by default with S3 managed keys. +S3 TableBuckets have SSE (server-side encryption with AES-256) enabled by default with S3 managed keys. You can also bring your own KMS key for KMS-SSE or have S3 create a KMS key for you. If a bucket is encrypted with KMS, grant functions on the bucket will also grant access -to the TableBucket's associated KMS key +to the TableBucket's associated KMS key. ```ts // Provide a user defined KMS Key: @@ -89,17 +89,21 @@ const encryptedBucket = new TableBucket(scope, 'EncryptedTableBucket', { // This account principal will also receive kms:Decrypt access to the KMS key encryptedBucket.grantRead(new iam.AccountPrincipal('123456789012'), '*'); +// Use S3 managed server side encryption (default) +const encryptedBucketDefault = new TableBucket(scope, 'EncryptedTableBucketDefault', { + tableBucketName: 'table-bucket-3', + encryption: TableBucketEncryption.S3_MANAGED, // Uses AES-256 encryption by default +}); +``` + +When using KMS encryption (`TableBucketEncryption.KMS`), if no encryption key is provided, CDK will automatically create a new KMS key for the table bucket with necessary permissions. + +```ts // If no key is provided, one will be created automatically const encryptedBucketAuto = new TableBucket(scope, 'EncryptedTableBucketAuto', { tableBucketName: 'table-bucket-2', encryption: TableBucketEncryption.KMS, }); - -// Use S3 managed server side encryption (default) -const encryptedBucketDefault = new TableBucket(scope, 'EncryptedTableBucketDefault', { - tableBucketName: 'table-bucket-3', - encryption: TableBucketEncryption.S3_MANAGED, -}); ``` ## Coming Soon From e050d01cc32120a6e2665a1da589642d03e6a3d9 Mon Sep 17 00:00:00 2001 From: Soham Kulkarni Date: Thu, 8 May 2025 13:31:47 -0700 Subject: [PATCH 7/7] Add encryption context condition to maintenance allowlist --- .../aws-s3tables-alpha/lib/table-bucket.ts | 13 ++++++++-- .../OnlyKMSEncryptionTypeTest.assets.json | 4 ++-- .../OnlyKMSEncryptionTypeTest.template.json | 24 +++++++++++++++++++ ...faultTestDeployAssert2F88D4F9.assets.json} | 4 ++-- ...ultTestDeployAssert2F88D4F9.template.json} | 0 .../integ.json | 6 ++--- .../manifest.json | 20 ++++++++-------- .../tree.json | 2 +- .../test/table-bucket-encryption.test.ts | 16 +++++++++++++ 9 files changed, 69 insertions(+), 20 deletions(-) rename packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/{TableBucketWithGrantIntegTestDefaultTestDeployAssert60E58AB6.assets.json => TableBucketEncryptionIntegTestDefaultTestDeployAssert2F88D4F9.assets.json} (76%) rename packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/{TableBucketWithGrantIntegTestDefaultTestDeployAssert60E58AB6.template.json => TableBucketEncryptionIntegTestDefaultTestDeployAssert2F88D4F9.template.json} (100%) 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 7677d8cbdd489..f7d870376473a 100644 --- a/packages/@aws-cdk/aws-s3tables-alpha/lib/table-bucket.ts +++ b/packages/@aws-cdk/aws-s3tables-alpha/lib/table-bucket.ts @@ -640,7 +640,7 @@ export class TableBucket extends TableBucketBase { description: `Created by ${this.node.path}`, enableKeyRotation: true, }); - this.allowTablesMaintenanceAccessToKey(key); + this.allowTablesMaintenanceAccessToKey(key, props.tableBucketName); } return { bucketEncryption: { @@ -671,7 +671,11 @@ export class TableBucket extends TableBucketBase { * @see https://docs.aws.amazon.com/AmazonS3/latest/userguide/s3-tables-kms-permissions.html * @param encryptionKey The key to provide access to */ - private allowTablesMaintenanceAccessToKey(encryptionKey: kms.IKey) { + private allowTablesMaintenanceAccessToKey(encryptionKey: kms.IKey, tableBucketName: string) { + const region = this.stack.region; + const account = this.stack.account; + const partition = this.stack.partition; + encryptionKey.addToResourcePolicy(new iam.PolicyStatement({ sid: 'AllowS3TablesMaintenanceAccess', effect: iam.Effect.ALLOW, @@ -683,6 +687,11 @@ export class TableBucket extends TableBucketBase { 'kms:Decrypt', ], resources: ['*'], + conditions: { + StringLike: { + 'kms:EncryptionContext:aws:s3:arn': `arn:${partition}:s3tables:${region}:${account}:bucket/${tableBucketName}/*`, + }, + }, })); } } diff --git a/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/OnlyKMSEncryptionTypeTest.assets.json b/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/OnlyKMSEncryptionTypeTest.assets.json index e20052571272a..56bc15fe3eefc 100644 --- a/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/OnlyKMSEncryptionTypeTest.assets.json +++ b/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/OnlyKMSEncryptionTypeTest.assets.json @@ -1,7 +1,7 @@ { "version": "41.0.0", "files": { - "610424a8b18bb9d397c2cc1bc2b0bfd9192556a38b603fc70f7140e91fd9a224": { + "7af1ffdaa9c46e7d8a539a46d562c407a37cf9c82ad9735d9a005cbf62345c08": { "displayName": "OnlyKMSEncryptionTypeTest Template", "source": { "path": "OnlyKMSEncryptionTypeTest.template.json", @@ -10,7 +10,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "610424a8b18bb9d397c2cc1bc2b0bfd9192556a38b603fc70f7140e91fd9a224.json", + "objectKey": "7af1ffdaa9c46e7d8a539a46d562c407a37cf9c82ad9735d9a005cbf62345c08.json", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } diff --git a/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/OnlyKMSEncryptionTypeTest.template.json b/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/OnlyKMSEncryptionTypeTest.template.json index 4e4f58075acf6..ce9a60b2b32fc 100644 --- a/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/OnlyKMSEncryptionTypeTest.template.json +++ b/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/OnlyKMSEncryptionTypeTest.template.json @@ -35,6 +35,30 @@ "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/integ-tb-kms-encryption-type-only/*" + ] + ] + } + } + }, "Effect": "Allow", "Principal": { "Service": "maintenance.s3tables.amazonaws.com" diff --git a/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/TableBucketWithGrantIntegTestDefaultTestDeployAssert60E58AB6.assets.json b/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/TableBucketEncryptionIntegTestDefaultTestDeployAssert2F88D4F9.assets.json similarity index 76% rename from packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/TableBucketWithGrantIntegTestDefaultTestDeployAssert60E58AB6.assets.json rename to packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/TableBucketEncryptionIntegTestDefaultTestDeployAssert2F88D4F9.assets.json index c3d07bfb04855..097ed1579a551 100644 --- a/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/TableBucketWithGrantIntegTestDefaultTestDeployAssert60E58AB6.assets.json +++ b/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/TableBucketEncryptionIntegTestDefaultTestDeployAssert2F88D4F9.assets.json @@ -2,9 +2,9 @@ "version": "41.0.0", "files": { "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { - "displayName": "TableBucketWithGrantIntegTestDefaultTestDeployAssert60E58AB6 Template", + "displayName": "TableBucketEncryptionIntegTestDefaultTestDeployAssert2F88D4F9 Template", "source": { - "path": "TableBucketWithGrantIntegTestDefaultTestDeployAssert60E58AB6.template.json", + "path": "TableBucketEncryptionIntegTestDefaultTestDeployAssert2F88D4F9.template.json", "packaging": "file" }, "destinations": { diff --git a/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/TableBucketWithGrantIntegTestDefaultTestDeployAssert60E58AB6.template.json b/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/TableBucketEncryptionIntegTestDefaultTestDeployAssert2F88D4F9.template.json similarity index 100% rename from packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/TableBucketWithGrantIntegTestDefaultTestDeployAssert60E58AB6.template.json rename to packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/TableBucketEncryptionIntegTestDefaultTestDeployAssert2F88D4F9.template.json diff --git a/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/integ.json b/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/integ.json index 982b13c196e18..d5e6be88fdb65 100644 --- a/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/integ.json +++ b/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/integ.json @@ -1,15 +1,15 @@ { "version": "41.0.0", "testCases": { - "TableBucketWithGrantIntegTest/DefaultTest": { + "TableBucketEncryptionIntegTest/DefaultTest": { "stacks": [ "OnlyEncryptionKeyTest", "OnlyKMSEncryptionTypeTest", "KeyWithKMSEncryptionTypeTest", "OnlyS3ManagedEncryptionTest" ], - "assertionStack": "TableBucketWithGrantIntegTest/DefaultTest/DeployAssert", - "assertionStackName": "TableBucketWithGrantIntegTestDefaultTestDeployAssert60E58AB6" + "assertionStack": "TableBucketEncryptionIntegTest/DefaultTest/DeployAssert", + "assertionStackName": "TableBucketEncryptionIntegTestDefaultTestDeployAssert2F88D4F9" } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/manifest.json b/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/manifest.json index 60e50c64b831e..0b6cc95be943d 100644 --- a/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/manifest.json +++ b/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/manifest.json @@ -90,7 +90,7 @@ "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}/610424a8b18bb9d397c2cc1bc2b0bfd9192556a38b603fc70f7140e91fd9a224.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/7af1ffdaa9c46e7d8a539a46d562c407a37cf9c82ad9735d9a005cbf62345c08.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ @@ -280,19 +280,19 @@ }, "displayName": "OnlyS3ManagedEncryptionTest" }, - "TableBucketWithGrantIntegTestDefaultTestDeployAssert60E58AB6.assets": { + "TableBucketEncryptionIntegTestDefaultTestDeployAssert2F88D4F9.assets": { "type": "cdk:asset-manifest", "properties": { - "file": "TableBucketWithGrantIntegTestDefaultTestDeployAssert60E58AB6.assets.json", + "file": "TableBucketEncryptionIntegTestDefaultTestDeployAssert2F88D4F9.assets.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" } }, - "TableBucketWithGrantIntegTestDefaultTestDeployAssert60E58AB6": { + "TableBucketEncryptionIntegTestDefaultTestDeployAssert2F88D4F9": { "type": "aws:cloudformation:stack", "environment": "aws://unknown-account/unknown-region", "properties": { - "templateFile": "TableBucketWithGrantIntegTestDefaultTestDeployAssert60E58AB6.template.json", + "templateFile": "TableBucketEncryptionIntegTestDefaultTestDeployAssert2F88D4F9.template.json", "terminationProtection": false, "validateOnSynth": false, "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", @@ -301,7 +301,7 @@ "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ - "TableBucketWithGrantIntegTestDefaultTestDeployAssert60E58AB6.assets" + "TableBucketEncryptionIntegTestDefaultTestDeployAssert2F88D4F9.assets" ], "lookupRole": { "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", @@ -310,23 +310,23 @@ } }, "dependencies": [ - "TableBucketWithGrantIntegTestDefaultTestDeployAssert60E58AB6.assets" + "TableBucketEncryptionIntegTestDefaultTestDeployAssert2F88D4F9.assets" ], "metadata": { - "/TableBucketWithGrantIntegTest/DefaultTest/DeployAssert/BootstrapVersion": [ + "/TableBucketEncryptionIntegTest/DefaultTest/DeployAssert/BootstrapVersion": [ { "type": "aws:cdk:logicalId", "data": "BootstrapVersion" } ], - "/TableBucketWithGrantIntegTest/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + "/TableBucketEncryptionIntegTest/DefaultTest/DeployAssert/CheckBootstrapVersion": [ { "type": "aws:cdk:logicalId", "data": "CheckBootstrapVersion" } ] }, - "displayName": "TableBucketWithGrantIntegTest/DefaultTest/DeployAssert" + "displayName": "TableBucketEncryptionIntegTest/DefaultTest/DeployAssert" }, "Tree": { "type": "cdk:tree", diff --git a/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/tree.json b/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/tree.json index 4fc2711f9b52c..2c9a45b9e28e1 100644 --- a/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/tree.json +++ b/packages/@aws-cdk/aws-s3tables-alpha/test/integration/integ.table-bucket-encryption.js.snapshot/tree.json @@ -1 +1 @@ -{"version":"tree-0.1","tree":{"id":"App","path":"","children":{"OnlyEncryptionKeyTest":{"id":"OnlyEncryptionKeyTest","path":"OnlyEncryptionKeyTest","children":{"Key":{"id":"Key","path":"OnlyEncryptionKeyTest/Key","children":{"Resource":{"id":"Resource","path":"OnlyEncryptionKeyTest/Key/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":"*"}],"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":["*"]}},"OnlyEncryptionKeyTest":{"id":"OnlyEncryptionKeyTest","path":"OnlyEncryptionKeyTest/OnlyEncryptionKeyTest","children":{"OnlyEncryptionKeyTest":{"id":"OnlyEncryptionKeyTest","path":"OnlyEncryptionKeyTest/OnlyEncryptionKeyTest/OnlyEncryptionKeyTest","attributes":{"aws:cdk:cloudformation:type":"AWS::S3Tables::TableBucket","aws:cdk:cloudformation:props":{"encryptionConfiguration":{"kmsKeyArn":{"Fn::GetAtt":["Key961B73FD","Arn"]},"sseAlgorithm":"aws:kms"},"tableBucketName":"integ-tb-key-only","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":"OnlyEncryptionKeyTest/BootstrapVersion","constructInfo":{"fqn":"aws-cdk-lib.CfnParameter","version":"0.0.0"}},"CheckBootstrapVersion":{"id":"CheckBootstrapVersion","path":"OnlyEncryptionKeyTest/CheckBootstrapVersion","constructInfo":{"fqn":"aws-cdk-lib.CfnRule","version":"0.0.0"}}},"constructInfo":{"fqn":"aws-cdk-lib.Stack","version":"0.0.0"}},"OnlyKMSEncryptionTypeTest":{"id":"OnlyKMSEncryptionTypeTest","path":"OnlyKMSEncryptionTypeTest","children":{"OnlyKMSEncryptionTypeTest":{"id":"OnlyKMSEncryptionTypeTest","path":"OnlyKMSEncryptionTypeTest/OnlyKMSEncryptionTypeTest","children":{"Key":{"id":"Key","path":"OnlyKMSEncryptionTypeTest/OnlyKMSEncryptionTypeTest/Key","children":{"Resource":{"id":"Resource","path":"OnlyKMSEncryptionTypeTest/OnlyKMSEncryptionTypeTest/Key/Resource","attributes":{"aws:cdk:cloudformation:type":"AWS::KMS::Key","aws:cdk:cloudformation:props":{"description":"Created by OnlyKMSEncryptionTypeTest/OnlyKMSEncryptionTypeTest","enableKeyRotation":true,"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"],"Effect":"Allow","Principal":{"Service":"maintenance.s3tables.amazonaws.com"},"Resource":"*","Sid":"AllowS3TablesMaintenanceAccess"}],"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":[{"description":"*","enableKeyRotation":true}]}},"OnlyKMSEncryptionTypeTest":{"id":"OnlyKMSEncryptionTypeTest","path":"OnlyKMSEncryptionTypeTest/OnlyKMSEncryptionTypeTest/OnlyKMSEncryptionTypeTest","attributes":{"aws:cdk:cloudformation:type":"AWS::S3Tables::TableBucket","aws:cdk:cloudformation:props":{"encryptionConfiguration":{"kmsKeyArn":{"Fn::GetAtt":["OnlyKMSEncryptionTypeTestKey190897FD","Arn"]},"sseAlgorithm":"aws:kms"},"tableBucketName":"integ-tb-kms-encryption-type-only","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":"OnlyKMSEncryptionTypeTest/BootstrapVersion","constructInfo":{"fqn":"aws-cdk-lib.CfnParameter","version":"0.0.0"}},"CheckBootstrapVersion":{"id":"CheckBootstrapVersion","path":"OnlyKMSEncryptionTypeTest/CheckBootstrapVersion","constructInfo":{"fqn":"aws-cdk-lib.CfnRule","version":"0.0.0"}}},"constructInfo":{"fqn":"aws-cdk-lib.Stack","version":"0.0.0"}},"KeyWithKMSEncryptionTypeTest":{"id":"KeyWithKMSEncryptionTypeTest","path":"KeyWithKMSEncryptionTypeTest","children":{"Key":{"id":"Key","path":"KeyWithKMSEncryptionTypeTest/Key","children":{"Resource":{"id":"Resource","path":"KeyWithKMSEncryptionTypeTest/Key/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":"*"}],"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":["*"]}},"KeyWithKMSEncryptionTypeTest":{"id":"KeyWithKMSEncryptionTypeTest","path":"KeyWithKMSEncryptionTypeTest/KeyWithKMSEncryptionTypeTest","children":{"KeyWithKMSEncryptionTypeTest":{"id":"KeyWithKMSEncryptionTypeTest","path":"KeyWithKMSEncryptionTypeTest/KeyWithKMSEncryptionTypeTest/KeyWithKMSEncryptionTypeTest","attributes":{"aws:cdk:cloudformation:type":"AWS::S3Tables::TableBucket","aws:cdk:cloudformation:props":{"encryptionConfiguration":{"kmsKeyArn":{"Fn::GetAtt":["Key961B73FD","Arn"]},"sseAlgorithm":"aws:kms"},"tableBucketName":"integ-tb-key-with-type","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":"KeyWithKMSEncryptionTypeTest/BootstrapVersion","constructInfo":{"fqn":"aws-cdk-lib.CfnParameter","version":"0.0.0"}},"CheckBootstrapVersion":{"id":"CheckBootstrapVersion","path":"KeyWithKMSEncryptionTypeTest/CheckBootstrapVersion","constructInfo":{"fqn":"aws-cdk-lib.CfnRule","version":"0.0.0"}}},"constructInfo":{"fqn":"aws-cdk-lib.Stack","version":"0.0.0"}},"OnlyS3ManagedEncryptionTest":{"id":"OnlyS3ManagedEncryptionTest","path":"OnlyS3ManagedEncryptionTest","children":{"OnlyS3ManagedEncryptionTest":{"id":"OnlyS3ManagedEncryptionTest","path":"OnlyS3ManagedEncryptionTest/OnlyS3ManagedEncryptionTest","children":{"OnlyS3ManagedEncryptionTest":{"id":"OnlyS3ManagedEncryptionTest","path":"OnlyS3ManagedEncryptionTest/OnlyS3ManagedEncryptionTest/OnlyS3ManagedEncryptionTest","attributes":{"aws:cdk:cloudformation:type":"AWS::S3Tables::TableBucket","aws:cdk:cloudformation:props":{"encryptionConfiguration":{"sseAlgorithm":"AES256"},"tableBucketName":"integ-tb-s3-managed-encryption-type-only","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":"OnlyS3ManagedEncryptionTest/BootstrapVersion","constructInfo":{"fqn":"aws-cdk-lib.CfnParameter","version":"0.0.0"}},"CheckBootstrapVersion":{"id":"CheckBootstrapVersion","path":"OnlyS3ManagedEncryptionTest/CheckBootstrapVersion","constructInfo":{"fqn":"aws-cdk-lib.CfnRule","version":"0.0.0"}}},"constructInfo":{"fqn":"aws-cdk-lib.Stack","version":"0.0.0"}},"TableBucketWithGrantIntegTest":{"id":"TableBucketWithGrantIntegTest","path":"TableBucketWithGrantIntegTest","children":{"DefaultTest":{"id":"DefaultTest","path":"TableBucketWithGrantIntegTest/DefaultTest","children":{"Default":{"id":"Default","path":"TableBucketWithGrantIntegTest/DefaultTest/Default","constructInfo":{"fqn":"constructs.Construct","version":"10.4.2"}},"DeployAssert":{"id":"DeployAssert","path":"TableBucketWithGrantIntegTest/DefaultTest/DeployAssert","children":{"BootstrapVersion":{"id":"BootstrapVersion","path":"TableBucketWithGrantIntegTest/DefaultTest/DeployAssert/BootstrapVersion","constructInfo":{"fqn":"aws-cdk-lib.CfnParameter","version":"0.0.0"}},"CheckBootstrapVersion":{"id":"CheckBootstrapVersion","path":"TableBucketWithGrantIntegTest/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 +{"version":"tree-0.1","tree":{"id":"App","path":"","children":{"OnlyEncryptionKeyTest":{"id":"OnlyEncryptionKeyTest","path":"OnlyEncryptionKeyTest","children":{"Key":{"id":"Key","path":"OnlyEncryptionKeyTest/Key","children":{"Resource":{"id":"Resource","path":"OnlyEncryptionKeyTest/Key/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":"*"}],"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":["*"]}},"OnlyEncryptionKeyTest":{"id":"OnlyEncryptionKeyTest","path":"OnlyEncryptionKeyTest/OnlyEncryptionKeyTest","children":{"OnlyEncryptionKeyTest":{"id":"OnlyEncryptionKeyTest","path":"OnlyEncryptionKeyTest/OnlyEncryptionKeyTest/OnlyEncryptionKeyTest","attributes":{"aws:cdk:cloudformation:type":"AWS::S3Tables::TableBucket","aws:cdk:cloudformation:props":{"encryptionConfiguration":{"kmsKeyArn":{"Fn::GetAtt":["Key961B73FD","Arn"]},"sseAlgorithm":"aws:kms"},"tableBucketName":"integ-tb-key-only","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":"OnlyEncryptionKeyTest/BootstrapVersion","constructInfo":{"fqn":"aws-cdk-lib.CfnParameter","version":"0.0.0"}},"CheckBootstrapVersion":{"id":"CheckBootstrapVersion","path":"OnlyEncryptionKeyTest/CheckBootstrapVersion","constructInfo":{"fqn":"aws-cdk-lib.CfnRule","version":"0.0.0"}}},"constructInfo":{"fqn":"aws-cdk-lib.Stack","version":"0.0.0"}},"OnlyKMSEncryptionTypeTest":{"id":"OnlyKMSEncryptionTypeTest","path":"OnlyKMSEncryptionTypeTest","children":{"OnlyKMSEncryptionTypeTest":{"id":"OnlyKMSEncryptionTypeTest","path":"OnlyKMSEncryptionTypeTest/OnlyKMSEncryptionTypeTest","children":{"Key":{"id":"Key","path":"OnlyKMSEncryptionTypeTest/OnlyKMSEncryptionTypeTest/Key","children":{"Resource":{"id":"Resource","path":"OnlyKMSEncryptionTypeTest/OnlyKMSEncryptionTypeTest/Key/Resource","attributes":{"aws:cdk:cloudformation:type":"AWS::KMS::Key","aws:cdk:cloudformation:props":{"description":"Created by OnlyKMSEncryptionTypeTest/OnlyKMSEncryptionTypeTest","enableKeyRotation":true,"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/integ-tb-kms-encryption-type-only/*"]]}}},"Effect":"Allow","Principal":{"Service":"maintenance.s3tables.amazonaws.com"},"Resource":"*","Sid":"AllowS3TablesMaintenanceAccess"}],"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":[{"description":"*","enableKeyRotation":true}]}},"OnlyKMSEncryptionTypeTest":{"id":"OnlyKMSEncryptionTypeTest","path":"OnlyKMSEncryptionTypeTest/OnlyKMSEncryptionTypeTest/OnlyKMSEncryptionTypeTest","attributes":{"aws:cdk:cloudformation:type":"AWS::S3Tables::TableBucket","aws:cdk:cloudformation:props":{"encryptionConfiguration":{"kmsKeyArn":{"Fn::GetAtt":["OnlyKMSEncryptionTypeTestKey190897FD","Arn"]},"sseAlgorithm":"aws:kms"},"tableBucketName":"integ-tb-kms-encryption-type-only","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":"OnlyKMSEncryptionTypeTest/BootstrapVersion","constructInfo":{"fqn":"aws-cdk-lib.CfnParameter","version":"0.0.0"}},"CheckBootstrapVersion":{"id":"CheckBootstrapVersion","path":"OnlyKMSEncryptionTypeTest/CheckBootstrapVersion","constructInfo":{"fqn":"aws-cdk-lib.CfnRule","version":"0.0.0"}}},"constructInfo":{"fqn":"aws-cdk-lib.Stack","version":"0.0.0"}},"KeyWithKMSEncryptionTypeTest":{"id":"KeyWithKMSEncryptionTypeTest","path":"KeyWithKMSEncryptionTypeTest","children":{"Key":{"id":"Key","path":"KeyWithKMSEncryptionTypeTest/Key","children":{"Resource":{"id":"Resource","path":"KeyWithKMSEncryptionTypeTest/Key/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":"*"}],"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":["*"]}},"KeyWithKMSEncryptionTypeTest":{"id":"KeyWithKMSEncryptionTypeTest","path":"KeyWithKMSEncryptionTypeTest/KeyWithKMSEncryptionTypeTest","children":{"KeyWithKMSEncryptionTypeTest":{"id":"KeyWithKMSEncryptionTypeTest","path":"KeyWithKMSEncryptionTypeTest/KeyWithKMSEncryptionTypeTest/KeyWithKMSEncryptionTypeTest","attributes":{"aws:cdk:cloudformation:type":"AWS::S3Tables::TableBucket","aws:cdk:cloudformation:props":{"encryptionConfiguration":{"kmsKeyArn":{"Fn::GetAtt":["Key961B73FD","Arn"]},"sseAlgorithm":"aws:kms"},"tableBucketName":"integ-tb-key-with-type","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":"KeyWithKMSEncryptionTypeTest/BootstrapVersion","constructInfo":{"fqn":"aws-cdk-lib.CfnParameter","version":"0.0.0"}},"CheckBootstrapVersion":{"id":"CheckBootstrapVersion","path":"KeyWithKMSEncryptionTypeTest/CheckBootstrapVersion","constructInfo":{"fqn":"aws-cdk-lib.CfnRule","version":"0.0.0"}}},"constructInfo":{"fqn":"aws-cdk-lib.Stack","version":"0.0.0"}},"OnlyS3ManagedEncryptionTest":{"id":"OnlyS3ManagedEncryptionTest","path":"OnlyS3ManagedEncryptionTest","children":{"OnlyS3ManagedEncryptionTest":{"id":"OnlyS3ManagedEncryptionTest","path":"OnlyS3ManagedEncryptionTest/OnlyS3ManagedEncryptionTest","children":{"OnlyS3ManagedEncryptionTest":{"id":"OnlyS3ManagedEncryptionTest","path":"OnlyS3ManagedEncryptionTest/OnlyS3ManagedEncryptionTest/OnlyS3ManagedEncryptionTest","attributes":{"aws:cdk:cloudformation:type":"AWS::S3Tables::TableBucket","aws:cdk:cloudformation:props":{"encryptionConfiguration":{"sseAlgorithm":"AES256"},"tableBucketName":"integ-tb-s3-managed-encryption-type-only","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":"OnlyS3ManagedEncryptionTest/BootstrapVersion","constructInfo":{"fqn":"aws-cdk-lib.CfnParameter","version":"0.0.0"}},"CheckBootstrapVersion":{"id":"CheckBootstrapVersion","path":"OnlyS3ManagedEncryptionTest/CheckBootstrapVersion","constructInfo":{"fqn":"aws-cdk-lib.CfnRule","version":"0.0.0"}}},"constructInfo":{"fqn":"aws-cdk-lib.Stack","version":"0.0.0"}},"TableBucketEncryptionIntegTest":{"id":"TableBucketEncryptionIntegTest","path":"TableBucketEncryptionIntegTest","children":{"DefaultTest":{"id":"DefaultTest","path":"TableBucketEncryptionIntegTest/DefaultTest","children":{"Default":{"id":"Default","path":"TableBucketEncryptionIntegTest/DefaultTest/Default","constructInfo":{"fqn":"constructs.Construct","version":"10.4.2"}},"DeployAssert":{"id":"DeployAssert","path":"TableBucketEncryptionIntegTest/DefaultTest/DeployAssert","children":{"BootstrapVersion":{"id":"BootstrapVersion","path":"TableBucketEncryptionIntegTest/DefaultTest/DeployAssert/BootstrapVersion","constructInfo":{"fqn":"aws-cdk-lib.CfnParameter","version":"0.0.0"}},"CheckBootstrapVersion":{"id":"CheckBootstrapVersion","path":"TableBucketEncryptionIntegTest/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/table-bucket-encryption.test.ts b/packages/@aws-cdk/aws-s3tables-alpha/test/table-bucket-encryption.test.ts index 10378a3d99fe4..fc9d6b43c5d4a 100644 --- a/packages/@aws-cdk/aws-s3tables-alpha/test/table-bucket-encryption.test.ts +++ b/packages/@aws-cdk/aws-s3tables-alpha/test/table-bucket-encryption.test.ts @@ -299,6 +299,22 @@ describe('TableBucket with encryption', () => { 'Service': 'maintenance.s3tables.amazonaws.com', }, 'Resource': '*', + 'Condition': { + 'StringLike': { + 'kms:EncryptionContext:aws:s3:arn': { + 'Fn::Join': ['', + [ + 'arn:', + { 'Ref': 'AWS::Partition' }, + ':s3tables:', + { 'Ref': 'AWS::Region' }, + ':', + { 'Ref': 'AWS::AccountId' }, + `:bucket/${TABLE_BUCKET_NAME}/*`, + ]], + }, + }, + }, }, ]), },