From d46b5359cf0c4960b6aeae989b72278df7e49a4e Mon Sep 17 00:00:00 2001 From: Michael Vecchione Date: Thu, 26 Oct 2023 17:09:15 -0400 Subject: [PATCH] feat(dynamodb): add tagging support to TableV2 (#27649) This change adds the ability to tag GlobalTable replicas created using the TableV2 construct. The top level "tags" will apply tags to the primary table while tags for each replica can be specified in the respective replica definition. Closes #27146. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../aws-cdk-global-table.template.json | 24 +++++++- .../tree.json | 24 +++++++- .../test/integ.table-v2-global.ts | 3 + packages/aws-cdk-lib/aws-dynamodb/README.md | 25 ++++++++ .../aws-cdk-lib/aws-dynamodb/lib/table-v2.ts | 11 +++- .../aws-dynamodb/test/table-v2.test.ts | 58 +++++++++++++++++++ 6 files changed, 138 insertions(+), 7 deletions(-) diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.table-v2-global.js.snapshot/aws-cdk-global-table.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.table-v2-global.js.snapshot/aws-cdk-global-table.template.json index a441ef44c2b15..c108cf892122a 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.table-v2-global.js.snapshot/aws-cdk-global-table.template.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.table-v2-global.js.snapshot/aws-cdk-global-table.template.json @@ -143,7 +143,13 @@ } }, "Region": "us-east-2", - "TableClass": "STANDARD_INFREQUENT_ACCESS" + "TableClass": "STANDARD_INFREQUENT_ACCESS", + "Tags": [ + { + "Key": "USE2ReplicaTagKey", + "Value": "USE2ReplicaTagValue" + } + ] }, { "ContributorInsightsSpecification": { @@ -173,7 +179,13 @@ "ReadCapacityUnits": 10 }, "Region": "us-west-2", - "TableClass": "STANDARD" + "TableClass": "STANDARD", + "Tags": [ + { + "Key": "USW2ReplicaTagKey", + "Value": "USW2ReplicaTagValue" + } + ] }, { "ContributorInsightsSpecification": { @@ -214,7 +226,13 @@ "ReadCapacityUnits": 10 }, "Region": "us-east-1", - "TableClass": "STANDARD_INFREQUENT_ACCESS" + "TableClass": "STANDARD_INFREQUENT_ACCESS", + "Tags": [ + { + "Key": "primaryTableTagKey", + "Value": "primaryTableTagValue" + } + ] } ], "SSESpecification": { diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.table-v2-global.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.table-v2-global.js.snapshot/tree.json index 88de97ce10b61..6c0d53c9afce4 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.table-v2-global.js.snapshot/tree.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.table-v2-global.js.snapshot/tree.json @@ -184,7 +184,13 @@ "targetValue": 70 } } - } + }, + "tags": [ + { + "key": "USE2ReplicaTagKey", + "value": "USE2ReplicaTagValue" + } + ] }, { "region": "us-west-2", @@ -214,7 +220,13 @@ }, "readProvisionedThroughputSettings": { "readCapacityUnits": 10 - } + }, + "tags": [ + { + "key": "USW2ReplicaTagKey", + "value": "USW2ReplicaTagValue" + } + ] }, { "region": "us-east-1", @@ -255,7 +267,13 @@ }, "readProvisionedThroughputSettings": { "readCapacityUnits": 10 - } + }, + "tags": [ + { + "key": "primaryTableTagKey", + "value": "primaryTableTagValue" + } + ] } ], "sseSpecification": { diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.table-v2-global.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.table-v2-global.ts index 0ccc09dcfad95..5a81eadcae96c 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.table-v2-global.ts +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.table-v2-global.ts @@ -52,6 +52,7 @@ class TestStack extends Stack { contributorInsights: false, }, }, + tags: [{ key: 'USE2ReplicaTagKey', value: 'USE2ReplicaTagValue' }], }, { region: 'us-west-2', @@ -62,8 +63,10 @@ class TestStack extends Stack { readCapacity: Capacity.fixed(15), }, }, + tags: [{ key: 'USW2ReplicaTagKey', value: 'USW2ReplicaTagValue' }], }, ], + tags: [{ key: 'primaryTableTagKey', value: 'primaryTableTagValue' }], }); } } diff --git a/packages/aws-cdk-lib/aws-dynamodb/README.md b/packages/aws-cdk-lib/aws-dynamodb/README.md index 7d32310774fa1..e35e679605236 100644 --- a/packages/aws-cdk-lib/aws-dynamodb/README.md +++ b/packages/aws-cdk-lib/aws-dynamodb/README.md @@ -588,6 +588,31 @@ const table = new dynamodb.TableV2(this, 'Table', { Further reading: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.TableClasses.html +## Tags + +You can add tags to a `TableV2` in several ways. By adding the tags to the construct itself it will apply the tags to the +primary table. +```ts +const table = new dynamodb.TableV2(this, 'Table', { + partitionKey: { name: 'pk', type: dynamodb.AttributeType.STRING }, + tags: [{key: 'primaryTableTagKey', value: 'primaryTableTagValue'}], +}); +``` + +You can also add tags to replica tables by specifying them within the replica table properties. + +```ts +const table = new dynamodb.TableV2(this, 'Table', { + partitionKey: { name: 'pk', type: dynamodb.AttributeType.STRING }, + replicas: [ + { + region: 'us-west-1', + tags: [{key: 'replicaTableTagKey', value: 'replicaTableTagValue'}] + } + ] +}); +``` + ## Referencing Existing Global Tables To reference an existing DynamoDB table in your CDK application, use the `TableV2.fromTableName`, `TableV2.fromTableArn`, or `TableV2.fromTableAttributes` diff --git a/packages/aws-cdk-lib/aws-dynamodb/lib/table-v2.ts b/packages/aws-cdk-lib/aws-dynamodb/lib/table-v2.ts index f2b37fbd038cd..b19a3eb00c3ac 100644 --- a/packages/aws-cdk-lib/aws-dynamodb/lib/table-v2.ts +++ b/packages/aws-cdk-lib/aws-dynamodb/lib/table-v2.ts @@ -11,7 +11,7 @@ import { import { TableBaseV2, ITableV2 } from './table-v2-base'; import { IStream } from '../../aws-kinesis'; import { IKey, Key } from '../../aws-kms'; -import { ArnFormat, Lazy, PhysicalName, RemovalPolicy, Stack, Token } from '../../core'; +import { ArnFormat, CfnTag, Lazy, PhysicalName, RemovalPolicy, Stack, Token } from '../../core'; const HASH_KEY_TYPE = 'HASH'; const RANGE_KEY_TYPE = 'RANGE'; @@ -114,6 +114,13 @@ export interface TableOptionsV2 { * @default - no Kinesis Data Stream */ readonly kinesisStream?: IStream; + + /** + * Tags to be applied to the table or replica table + * + * @default - no tags + */ + readonly tags?: CfnTag[]; } /** @@ -612,6 +619,7 @@ export class TableV2 extends TableBaseV2 { readProvisionedThroughputSettings: props.readCapacity ? props.readCapacity._renderReadCapacity() : this.readProvisioning, + tags: props.tags, }; } @@ -716,6 +724,7 @@ export class TableV2 extends TableBaseV2 { replicaTables.push(this.configureReplicaTable({ region: this.stack.region, kinesisStream: this.tableOptions.kinesisStream, + tags: this.tableOptions.tags, })); return replicaTables; diff --git a/packages/aws-cdk-lib/aws-dynamodb/test/table-v2.test.ts b/packages/aws-cdk-lib/aws-dynamodb/test/table-v2.test.ts index 896db2374b1bb..fc8db61eebbc4 100644 --- a/packages/aws-cdk-lib/aws-dynamodb/test/table-v2.test.ts +++ b/packages/aws-cdk-lib/aws-dynamodb/test/table-v2.test.ts @@ -547,6 +547,31 @@ describe('table', () => { }); }); + test('with tags', () => { + // GIVEN + const stack = new Stack(undefined, 'Stack', { env: { region: 'us-east-1' } }); + + // WHEN + new TableV2(stack, 'Table', { + partitionKey: { name: 'pk', type: AttributeType.STRING }, + replicas: [{ region: 'us-west-1' }], + tags: [{ key: 'tagKey', value: 'tagValue' }], + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::DynamoDB::GlobalTable', { + Replicas: [ + { + Region: 'us-west-1', + }, + { + Region: 'us-east-1', + Tags: [{ Key: 'tagKey', Value: 'tagValue' }], + }, + ], + }); + }); + test('with all properties configured', () => { // GIVEN const stack = new Stack(undefined, 'Stack', { env: { region: 'us-west-2' } }); @@ -605,6 +630,7 @@ describe('table', () => { contributorInsights: false, }, }, + tags: [{ key: 'USE1Key', value: 'USE1Value' }], }, { region: 'us-east-2', @@ -615,8 +641,10 @@ describe('table', () => { readCapacity: Capacity.fixed(15), }, }, + tags: [{ key: 'USE2Key', value: 'USE2Value' }], }, ], + tags: [{ key: 'USW2Key', value: 'USW2Value' }], }); // THEN @@ -748,6 +776,7 @@ describe('table', () => { KMSMasterKeyId: 'arn:aws:kms:us-east-1:123456789012:key/g24efbna-az9b-42ro-m3bp-cq249l94fca6', }, TableClass: 'STANDARD_INFREQUENT_ACCESS', + Tags: [{ Key: 'USE1Key', Value: 'USE1Value' }], }, { ContributorInsightsSpecification: { @@ -783,6 +812,7 @@ describe('table', () => { KMSMasterKeyId: 'arn:aws:kms:us-east-2:123456789012:key/g24efbna-az9b-42ro-m3bp-cq249l94fca6', }, TableClass: 'STANDARD', + Tags: [{ Key: 'USE2Key', Value: 'USE2Value' }], }, { ContributorInsightsSpecification: { @@ -833,6 +863,7 @@ describe('table', () => { }, }, TableClass: 'STANDARD_INFREQUENT_ACCESS', + Tags: [{ Key: 'USW2Key', Value: 'USW2Value' }], }, ], SSESpecification: { @@ -1079,6 +1110,33 @@ describe('replica tables', () => { }); }); + test('with tags', () => { + // GIVEN + const stack = new Stack(undefined, 'Stack', { env: { region: 'us-east-1' } }); + + // WHEN + new TableV2(stack, 'Table', { + partitionKey: { name: 'pk', type: AttributeType.STRING }, + replicas: [{ + region: 'us-west-1', + tags: [{ key: 'tagKey', value: 'tagValue' }], + }], + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::DynamoDB::GlobalTable', { + Replicas: [ + { + Region: 'us-west-1', + Tags: [{ Key: 'tagKey', Value: 'tagValue' }], + }, + { + Region: 'us-east-1', + }, + ], + }); + }); + test('with per-replica kinesis stream', () => { // GIVEN const stack = new Stack(undefined, 'Stack', { env: { region: 'us-west-2' } });