From da7c863c3b4f27007d16c497849a5cdf472cfd25 Mon Sep 17 00:00:00 2001 From: Lee Hannigan Date: Tue, 13 Aug 2024 17:58:39 +0100 Subject: [PATCH 1/7] Fix - Resource base policy for replica tables --- .../aws-cdk-lib/aws-dynamodb/lib/table-v2.ts | 5 +- .../aws-dynamodb/test/table-v2.test.ts | 178 ++++++++++++++++++ 2 files changed, 181 insertions(+), 2 deletions(-) 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 65e3c588968e5..21f2b7a5e584f 100644 --- a/packages/aws-cdk-lib/aws-dynamodb/lib/table-v2.ts +++ b/packages/aws-cdk-lib/aws-dynamodb/lib/table-v2.ts @@ -664,8 +664,9 @@ export class TableV2 extends TableBaseV2 { private configureReplicaTable(props: ReplicaTableProps): CfnGlobalTable.ReplicaSpecificationProperty { const pointInTimeRecovery = props.pointInTimeRecovery ?? this.tableOptions.pointInTimeRecovery; const contributorInsights = props.contributorInsights ?? this.tableOptions.contributorInsights; - const resourcePolicy = props.resourcePolicy ?? this.tableOptions.resourcePolicy; - + //const resourcePolicy = props.resourcePolicy ?? this.tableOptions.resourcePolicy; + //const resourcePolicy = props.resourcePolicy; + const resourcePolicy = (props.region === this.region ? this.tableOptions.resourcePolicy : props.resourcePolicy) || undefined; return { region: props.region, globalSecondaryIndexes: this.configureReplicaGlobalSecondaryIndexes(props.globalSecondaryIndexOptions), 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 63bbf3319b73e..512085b1002ae 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 @@ -3007,4 +3007,182 @@ test('Resource policy test', () => { }, ], }); +}); + +test('Add resource policy to local table only', () => { + // GIVEN + const stack = new Stack(undefined, 'Stack', { env: { region: 'eu-west-1' } }); + + const doc = new PolicyDocument({ + statements: [ + new PolicyStatement({ + actions: ['dynamodb:GetItem'], + principals: [new ArnPrincipal('arn:aws:iam::111122223333:user/foobar')], + resources: ['*'], + }), + ], + }); + + // WHEN + const table = new TableV2(stack, 'Table', { + partitionKey: { name: 'metric', type: AttributeType.STRING }, + resourcePolicy: doc, + replicas: [{ + region: 'eu-west-2', + }], + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::DynamoDB::GlobalTable', { + Replicas: [ + { + Region: 'eu-west-2', + }, + { + Region: 'eu-west-1', + ResourcePolicy: { + PolicyDocument: { + Statement: [ + { + Action: 'dynamodb:GetItem', + Effect: 'Allow', + Principal: { + AWS: 'arn:aws:iam::111122223333:user/foobar', + }, + Resource: '*', + }, + ], + Version: '2012-10-17', + }, + }, + }, + ], + }); +}); + +test('Add resource policy to replica table only', () => { + // GIVEN + const stack = new Stack(undefined, 'Stack', { env: { region: 'eu-west-1' } }); + + const doc = new PolicyDocument({ + statements: [ + new PolicyStatement({ + actions: ['dynamodb:GetItem'], + principals: [new ArnPrincipal('arn:aws:iam::111122223333:user/foobar')], + resources: ['*'], + }), + ], + }); + + // WHEN + const table = new TableV2(stack, 'Table', { + partitionKey: { name: 'metric', type: AttributeType.STRING }, + replicas: [{ + region: 'eu-west-2', + resourcePolicy: doc, + }], + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::DynamoDB::GlobalTable', { + Replicas: [ + { + Region: 'eu-west-2', + ResourcePolicy: { + PolicyDocument: { + Statement: [ + { + Action: 'dynamodb:GetItem', + Effect: 'Allow', + Principal: { + AWS: 'arn:aws:iam::111122223333:user/foobar', + }, + Resource: '*', + }, + ], + Version: '2012-10-17', + }, + }, + }, + { + Region: 'eu-west-1', + }, + ], + }); +}); + +test('Add two different resource policies to replicas', () => { + // GIVEN + const stack = new Stack(undefined, 'Stack', { env: { region: 'eu-west-1' } }); + + const doc1 = new PolicyDocument({ + statements: [ + new PolicyStatement({ + actions: ['dynamodb:GetItem'], + principals: [new ArnPrincipal('arn:aws:iam::111122223333:user/foobar')], + resources: ['*'], + }), + ], + }); + const doc2 = new PolicyDocument({ + statements: [ + new PolicyStatement({ + actions: ['dynamodb:DeleteItem'], + principals: [new ArnPrincipal('arn:aws:iam::111122223333:user/barfoo')], + resources: ['*'], + }), + ], + }); + + // WHEN + const table = new TableV2(stack, 'Table', { + partitionKey: { name: 'metric', type: AttributeType.STRING }, + resourcePolicy: doc1, + replicas: [{ + region: 'eu-west-2', + resourcePolicy: doc2, + }], + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::DynamoDB::GlobalTable', { + Replicas: [ + { + Region: 'eu-west-2', + ResourcePolicy: { + PolicyDocument: { + Statement: [ + { + Action: 'dynamodb:DeleteItem', + Effect: 'Allow', + Principal: { + AWS: 'arn:aws:iam::111122223333:user/barfoo', + }, + Resource: '*', + }, + ], + Version: '2012-10-17', + }, + }, + }, + { + Region: 'eu-west-1', + ResourcePolicy: { + PolicyDocument: { + Statement: [ + { + Action: 'dynamodb:GetItem', + Effect: 'Allow', + Principal: { + AWS: 'arn:aws:iam::111122223333:user/foobar', + }, + Resource: '*', + }, + ], + Version: '2012-10-17', + }, + } + }, + ], + }); }); \ No newline at end of file From 7c74c7812fe22033fe33122fdb09a50afbfc9bc8 Mon Sep 17 00:00:00 2001 From: Lee Hannigan Date: Tue, 13 Aug 2024 20:04:41 +0100 Subject: [PATCH 2/7] Fix - Added integ tests --- .../ResourcePolicyTest-v2.assets.json | 4 ++-- .../ResourcePolicyTest-v2.template.json | 8 +++++++- .../integ.dynamodb-v2.policy.js.snapshot/manifest.json | 2 +- .../test/integ.dynamodb-v2.policy.js.snapshot/tree.json | 8 +++++++- .../test/aws-dynamodb/test/integ.dynamodb-v2.policy.ts | 3 +++ packages/aws-cdk-lib/aws-dynamodb/test/table-v2.test.ts | 2 +- 6 files changed, 21 insertions(+), 6 deletions(-) diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy.js.snapshot/ResourcePolicyTest-v2.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy.js.snapshot/ResourcePolicyTest-v2.assets.json index 8193c8bf97527..f6f09ea4f987c 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy.js.snapshot/ResourcePolicyTest-v2.assets.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy.js.snapshot/ResourcePolicyTest-v2.assets.json @@ -1,7 +1,7 @@ { "version": "36.0.0", "files": { - "192dc5b63bb0eb2c99bc1ea8a8fe1237a00e5067ac672d4e4f0986700f476849": { + "0aaa25e74c3bd8b4c72922081490518294b93ff15f40df38ae16908cbe6d69c5": { "source": { "path": "ResourcePolicyTest-v2.template.json", "packaging": "file" @@ -9,7 +9,7 @@ "destinations": { "current_account-eu-west-1": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-eu-west-1", - "objectKey": "192dc5b63bb0eb2c99bc1ea8a8fe1237a00e5067ac672d4e4f0986700f476849.json", + "objectKey": "0aaa25e74c3bd8b4c72922081490518294b93ff15f40df38ae16908cbe6d69c5.json", "region": "eu-west-1", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-eu-west-1" } diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy.js.snapshot/ResourcePolicyTest-v2.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy.js.snapshot/ResourcePolicyTest-v2.template.json index 5932811ef4cf1..28240d04c34e3 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy.js.snapshot/ResourcePolicyTest-v2.template.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy.js.snapshot/ResourcePolicyTest-v2.template.json @@ -17,6 +17,9 @@ } ], "Replicas": [ + { + "Region": "eu-west-2" + }, { "Region": "eu-west-1", "ResourcePolicy": { @@ -46,7 +49,10 @@ } } } - ] + ], + "StreamSpecification": { + "StreamViewType": "NEW_AND_OLD_IMAGES" + } }, "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy.js.snapshot/manifest.json index 3c95c2ac52ac0..3fb08065139df 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy.js.snapshot/manifest.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy.js.snapshot/manifest.json @@ -18,7 +18,7 @@ "validateOnSynth": false, "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-eu-west-1", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-eu-west-1", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-eu-west-1/192dc5b63bb0eb2c99bc1ea8a8fe1237a00e5067ac672d4e4f0986700f476849.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-eu-west-1/0aaa25e74c3bd8b4c72922081490518294b93ff15f40df38ae16908cbe6d69c5.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy.js.snapshot/tree.json index 8417b42605972..19c7820ebfb63 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy.js.snapshot/tree.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy.js.snapshot/tree.json @@ -32,6 +32,9 @@ } ], "replicas": [ + { + "region": "eu-west-2" + }, { "region": "eu-west-1", "resourcePolicy": { @@ -61,7 +64,10 @@ } } } - ] + ], + "streamSpecification": { + "streamViewType": "NEW_AND_OLD_IMAGES" + } } }, "constructInfo": { diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy.ts index 5a30de692de21..b01305774e720 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy.ts +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy.ts @@ -31,6 +31,9 @@ class TestStack extends Stack { }); table.grantReadData(new iam.AccountPrincipal('123456789012')); + table.addReplica({ + region: 'eu-west-2', + }); } } 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 512085b1002ae..e095a2485ebf2 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 @@ -3181,7 +3181,7 @@ test('Add two different resource policies to replicas', () => { ], Version: '2012-10-17', }, - } + }, }, ], }); From 4accd0f1393e8c437b350ee2bda981fe6aac5ba1 Mon Sep 17 00:00:00 2001 From: Lee Hannigan Date: Wed, 21 Aug 2024 18:48:14 +0100 Subject: [PATCH 3/7] Removing comments from table-v2 --- packages/aws-cdk-lib/aws-dynamodb/lib/table-v2.ts | 2 -- 1 file changed, 2 deletions(-) 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 21f2b7a5e584f..1e01c22a5dfcb 100644 --- a/packages/aws-cdk-lib/aws-dynamodb/lib/table-v2.ts +++ b/packages/aws-cdk-lib/aws-dynamodb/lib/table-v2.ts @@ -664,8 +664,6 @@ export class TableV2 extends TableBaseV2 { private configureReplicaTable(props: ReplicaTableProps): CfnGlobalTable.ReplicaSpecificationProperty { const pointInTimeRecovery = props.pointInTimeRecovery ?? this.tableOptions.pointInTimeRecovery; const contributorInsights = props.contributorInsights ?? this.tableOptions.contributorInsights; - //const resourcePolicy = props.resourcePolicy ?? this.tableOptions.resourcePolicy; - //const resourcePolicy = props.resourcePolicy; const resourcePolicy = (props.region === this.region ? this.tableOptions.resourcePolicy : props.resourcePolicy) || undefined; return { region: props.region, From ce9f776d4942c1cf5130ac15659d1e07b1c7137f Mon Sep 17 00:00:00 2001 From: Lee Hannigan Date: Wed, 28 Aug 2024 11:41:05 +0100 Subject: [PATCH 4/7] Added feature flag for resource policy --- .../test/integ.dynamodb-v2.featureflag.ts | 46 +++++++++++++++++++ .../aws-cdk-lib/aws-dynamodb/lib/table-v2.ts | 13 +++++- 2 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.featureflag.ts diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.featureflag.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.featureflag.ts new file mode 100644 index 0000000000000..72f79c087b138 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.featureflag.ts @@ -0,0 +1,46 @@ +import { App, RemovalPolicy, Stack, StackProps } from 'aws-cdk-lib'; +import { Construct } from 'constructs'; +import * as dynamodb from 'aws-cdk-lib/aws-dynamodb'; +import * as iam from 'aws-cdk-lib/aws-iam'; +import { IntegTest } from '@aws-cdk/integ-tests-alpha'; + +const app = new App({ + postCliContext: { + '@aws-cdk/aws-dynamodb:resourcePolicyPerReplica': true, + }, +}); + +class TestStack extends Stack { + constructor(scope: Construct, id: string, props?: StackProps) { + super(scope, id, props); + + const docu = new iam.PolicyDocument({ + statements: [ + new iam.PolicyStatement({ + actions: ['dynamodb:*'], + principals: [new iam.AccountRootPrincipal()], + resources: ['*'], + }), + ], + }); + + // table with resource policy + new dynamodb.TableV2(this, 'TableTestV2', { + partitionKey: { + name: 'id', + type: dynamodb.AttributeType.STRING, + }, + removalPolicy: RemovalPolicy.DESTROY, + resourcePolicy: docu, + replicas: [{ + region: 'eu-west-2', + }], + }); + } +} + +const stack = new TestStack(app, 'ResourcePolicyTest-v2-ff', { env: { region: 'eu-west-1' } }); + +new IntegTest(app, 'table-v2-feature-flag', { + testCases: [stack], +}); \ No newline at end of file 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 1e01c22a5dfcb..bc0f0dfbaf49b 100644 --- a/packages/aws-cdk-lib/aws-dynamodb/lib/table-v2.ts +++ b/packages/aws-cdk-lib/aws-dynamodb/lib/table-v2.ts @@ -12,7 +12,8 @@ import { TableBaseV2, ITableV2 } from './table-v2-base'; import { PolicyDocument } from '../../aws-iam'; import { IStream } from '../../aws-kinesis'; import { IKey, Key } from '../../aws-kms'; -import { ArnFormat, CfnTag, Lazy, PhysicalName, RemovalPolicy, Stack, Token } from '../../core'; +import { ArnFormat, CfnTag, FeatureFlags, Lazy, PhysicalName, RemovalPolicy, Stack, Token } from '../../core'; +import * as cxapi from '../../../@aws-cdk/cx-api'; const HASH_KEY_TYPE = 'HASH'; const RANGE_KEY_TYPE = 'RANGE'; @@ -664,7 +665,15 @@ export class TableV2 extends TableBaseV2 { private configureReplicaTable(props: ReplicaTableProps): CfnGlobalTable.ReplicaSpecificationProperty { const pointInTimeRecovery = props.pointInTimeRecovery ?? this.tableOptions.pointInTimeRecovery; const contributorInsights = props.contributorInsights ?? this.tableOptions.contributorInsights; - const resourcePolicy = (props.region === this.region ? this.tableOptions.resourcePolicy : props.resourcePolicy) || undefined; + /* + * Feature flag set as the following may be a breaking change. + * @see https://github.com/aws/aws-cdk/pull/31097 + * @see https://github.com/aws/aws-cdk/blob/main/packages/%40aws-cdk/cx-api/FEATURE_FLAGS.md + */ + const resourcePolicy = FeatureFlags.of(this).isEnabled(cxapi.DYNAMODB_TABLEV2_RESOURCE_POLICY_PER_REPLICA) + ? (props.region === this.region ? this.tableOptions.resourcePolicy : props.resourcePolicy) || undefined + : props.resourcePolicy ?? this.tableOptions.resourcePolicy; + return { region: props.region, globalSecondaryIndexes: this.configureReplicaGlobalSecondaryIndexes(props.globalSecondaryIndexOptions), From 5721dd6d4937b8672ae6322729a095a21462be9d Mon Sep 17 00:00:00 2001 From: Lee Hannigan Date: Wed, 28 Aug 2024 12:04:04 +0100 Subject: [PATCH 5/7] Adding feature flag to cx-api --- .../aws-cdk-lib/aws-dynamodb/lib/table-v2.ts | 2 +- packages/aws-cdk-lib/cx-api/README.md | 18 ++++++++++++++++++ packages/aws-cdk-lib/cx-api/lib/features.ts | 16 ++++++++++++++++ 3 files changed, 35 insertions(+), 1 deletion(-) 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 bc0f0dfbaf49b..774aa3e5f114f 100644 --- a/packages/aws-cdk-lib/aws-dynamodb/lib/table-v2.ts +++ b/packages/aws-cdk-lib/aws-dynamodb/lib/table-v2.ts @@ -13,7 +13,7 @@ import { PolicyDocument } from '../../aws-iam'; import { IStream } from '../../aws-kinesis'; import { IKey, Key } from '../../aws-kms'; import { ArnFormat, CfnTag, FeatureFlags, Lazy, PhysicalName, RemovalPolicy, Stack, Token } from '../../core'; -import * as cxapi from '../../../@aws-cdk/cx-api'; +import * as cxapi from '../../cx-api'; const HASH_KEY_TYPE = 'HASH'; const RANGE_KEY_TYPE = 'RANGE'; diff --git a/packages/aws-cdk-lib/cx-api/README.md b/packages/aws-cdk-lib/cx-api/README.md index 06f364f9922f5..5892ee28d0fd2 100644 --- a/packages/aws-cdk-lib/cx-api/README.md +++ b/packages/aws-cdk-lib/cx-api/README.md @@ -375,4 +375,22 @@ _cdk.json_ "@aws-cdk/aws-stepfunctions-tasks:ecsReduceRunTaskPermissions": true } } +``` + +* `@aws-cdk/aws-dynamodb:resourcePolicyPerReplica` + +When enabled will allow you to specify a resource policy per replica, and not copy the source table policy to all replicas. + +- If this flag is not set, the default behavior for \`TableV2\` is to use the source regions \`resourcePolicy\` for all replicas. +- If this flag is set to `true`, the default behavior is that each replica defines it's own \`resourcePolicy\` independently. +- This is a feature flag as the old behavior was technically incorrect but users may have come to depend on it. + +_cdk.json_ + +```json +{ + "context": { + "@aws-cdk/aws-dynamodb:resourcePolicyPerReplica": true + } +} ``` \ No newline at end of file diff --git a/packages/aws-cdk-lib/cx-api/lib/features.ts b/packages/aws-cdk-lib/cx-api/lib/features.ts index 233529bf8fa5b..c3cb98023c825 100644 --- a/packages/aws-cdk-lib/cx-api/lib/features.ts +++ b/packages/aws-cdk-lib/cx-api/lib/features.ts @@ -105,6 +105,7 @@ export const EKS_NODEGROUP_NAME = '@aws-cdk/aws-eks:nodegroupNameAttribute'; export const EBS_DEFAULT_GP3 = '@aws-cdk/aws-ec2:ebsDefaultGp3Volume'; export const ECS_REMOVE_DEFAULT_DEPLOYMENT_ALARM = '@aws-cdk/aws-ecs:removeDefaultDeploymentAlarm'; export const LOG_API_RESPONSE_DATA_PROPERTY_TRUE_DEFAULT = '@aws-cdk/custom-resources:logApiResponseDataPropertyTrueDefault'; +export const DYNAMODB_TABLEV2_RESOURCE_POLICY_PER_REPLICA = '@aws-cdk/aws-dynamodb:resourcePolicyPerReplica'; export const FLAGS: Record = { ////////////////////////////////////////////////////////////////////// @@ -1092,6 +1093,21 @@ export const FLAGS: Record = { introducedIn: { v2: '2.145.0' }, recommendedValue: false, }, + + ////////////////////////////////////////////////////////////////////// + [DYNAMODB_TABLEV2_RESOURCE_POLICY_PER_REPLICA]: { + type: FlagType.BugFix, + summary: 'When enabled will allow you to specify a resource policy per replica, and not copy the source table policy to all replicas', + detailsMd: ` + If this flag is not set, the default behavior for \`TableV2\` is to use + the source regions \`resourcePolicy\` for all replicas. + + If this flag is set, the default behavior is that each replica defines it's own \`resourcePolicy\` independently. + + This is a feature flag as the old behavior was technically incorrect but users may have come to depend on it.`, + introducedIn: { v2: 'V2NEXT' }, + recommendedValue: true, + }, }; const CURRENT_MV = 'v2'; From 8cfe236d27ffaa5f5d0b881f2a3acf80e9db9075 Mon Sep 17 00:00:00 2001 From: Lee Hannigan Date: Wed, 28 Aug 2024 15:23:27 +0100 Subject: [PATCH 6/7] Update integ test --- .../ResourcePolicyTest-v2-ff.assets.json | 20 +++ .../ResourcePolicyTest-v2-ff.template.json | 89 ++++++++++ .../cdk.out | 1 + .../integ.json | 12 ++ .../manifest.json | 113 ++++++++++++ ...efaultTestDeployAssertB12000F0.assets.json | 19 ++ ...aultTestDeployAssertB12000F0.template.json | 36 ++++ .../tree.json | 168 ++++++++++++++++++ .../test/integ.dynamodb-v2.featureflag.ts | 3 - 9 files changed, 458 insertions(+), 3 deletions(-) create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.featureflag.js.snapshot/ResourcePolicyTest-v2-ff.assets.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.featureflag.js.snapshot/ResourcePolicyTest-v2-ff.template.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.featureflag.js.snapshot/cdk.out create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.featureflag.js.snapshot/integ.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.featureflag.js.snapshot/manifest.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.featureflag.js.snapshot/tablev2featureflagDefaultTestDeployAssertB12000F0.assets.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.featureflag.js.snapshot/tablev2featureflagDefaultTestDeployAssertB12000F0.template.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.featureflag.js.snapshot/tree.json diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.featureflag.js.snapshot/ResourcePolicyTest-v2-ff.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.featureflag.js.snapshot/ResourcePolicyTest-v2-ff.assets.json new file mode 100644 index 0000000000000..63f4e6e5855b7 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.featureflag.js.snapshot/ResourcePolicyTest-v2-ff.assets.json @@ -0,0 +1,20 @@ +{ + "version": "36.0.0", + "files": { + "b74e6e1a423f2cf0cf0b7100fdf79fe71d31e0999a6bfd72dad0a1ef9dad5f36": { + "source": { + "path": "ResourcePolicyTest-v2-ff.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-eu-west-1": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-eu-west-1", + "objectKey": "b74e6e1a423f2cf0cf0b7100fdf79fe71d31e0999a6bfd72dad0a1ef9dad5f36.json", + "region": "eu-west-1", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-eu-west-1" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.featureflag.js.snapshot/ResourcePolicyTest-v2-ff.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.featureflag.js.snapshot/ResourcePolicyTest-v2-ff.template.json new file mode 100644 index 0000000000000..e4c0343f7a2e4 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.featureflag.js.snapshot/ResourcePolicyTest-v2-ff.template.json @@ -0,0 +1,89 @@ +{ + "Resources": { + "TableTestV29E695762": { + "Type": "AWS::DynamoDB::GlobalTable", + "Properties": { + "AttributeDefinitions": [ + { + "AttributeName": "id", + "AttributeType": "S" + } + ], + "BillingMode": "PAY_PER_REQUEST", + "KeySchema": [ + { + "AttributeName": "id", + "KeyType": "HASH" + } + ], + "Replicas": [ + { + "Region": "eu-west-1", + "ResourcePolicy": { + "PolicyDocument": { + "Statement": [ + { + "Action": "dynamodb:*", + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:aws:iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + }, + "Resource": "*" + } + ], + "Version": "2012-10-17" + } + } + } + ] + }, + "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-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.featureflag.js.snapshot/cdk.out b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.featureflag.js.snapshot/cdk.out new file mode 100644 index 0000000000000..1f0068d32659a --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.featureflag.js.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"36.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.featureflag.js.snapshot/integ.json b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.featureflag.js.snapshot/integ.json new file mode 100644 index 0000000000000..fef784a96b766 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.featureflag.js.snapshot/integ.json @@ -0,0 +1,12 @@ +{ + "version": "36.0.0", + "testCases": { + "table-v2-feature-flag/DefaultTest": { + "stacks": [ + "ResourcePolicyTest-v2-ff" + ], + "assertionStack": "table-v2-feature-flag/DefaultTest/DeployAssert", + "assertionStackName": "tablev2featureflagDefaultTestDeployAssertB12000F0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.featureflag.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.featureflag.js.snapshot/manifest.json new file mode 100644 index 0000000000000..04c32066b5ff7 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.featureflag.js.snapshot/manifest.json @@ -0,0 +1,113 @@ +{ + "version": "36.0.0", + "artifacts": { + "ResourcePolicyTest-v2-ff.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "ResourcePolicyTest-v2-ff.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "ResourcePolicyTest-v2-ff": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/eu-west-1", + "properties": { + "templateFile": "ResourcePolicyTest-v2-ff.template.json", + "terminationProtection": false, + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-eu-west-1", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-eu-west-1", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-eu-west-1/b74e6e1a423f2cf0cf0b7100fdf79fe71d31e0999a6bfd72dad0a1ef9dad5f36.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "ResourcePolicyTest-v2-ff.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-eu-west-1", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "ResourcePolicyTest-v2-ff.assets" + ], + "metadata": { + "/ResourcePolicyTest-v2-ff/TableTestV2/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "TableTestV29E695762" + } + ], + "/ResourcePolicyTest-v2-ff/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/ResourcePolicyTest-v2-ff/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "ResourcePolicyTest-v2-ff" + }, + "tablev2featureflagDefaultTestDeployAssertB12000F0.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "tablev2featureflagDefaultTestDeployAssertB12000F0.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "tablev2featureflagDefaultTestDeployAssertB12000F0": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "tablev2featureflagDefaultTestDeployAssertB12000F0.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": [ + "tablev2featureflagDefaultTestDeployAssertB12000F0.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": [ + "tablev2featureflagDefaultTestDeployAssertB12000F0.assets" + ], + "metadata": { + "/table-v2-feature-flag/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/table-v2-feature-flag/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "table-v2-feature-flag/DefaultTest/DeployAssert" + }, + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.featureflag.js.snapshot/tablev2featureflagDefaultTestDeployAssertB12000F0.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.featureflag.js.snapshot/tablev2featureflagDefaultTestDeployAssertB12000F0.assets.json new file mode 100644 index 0000000000000..3eca22345899e --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.featureflag.js.snapshot/tablev2featureflagDefaultTestDeployAssertB12000F0.assets.json @@ -0,0 +1,19 @@ +{ + "version": "36.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "tablev2featureflagDefaultTestDeployAssertB12000F0.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-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.featureflag.js.snapshot/tablev2featureflagDefaultTestDeployAssertB12000F0.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.featureflag.js.snapshot/tablev2featureflagDefaultTestDeployAssertB12000F0.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.featureflag.js.snapshot/tablev2featureflagDefaultTestDeployAssertB12000F0.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-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.featureflag.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.featureflag.js.snapshot/tree.json new file mode 100644 index 0000000000000..2c9628257fb48 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.featureflag.js.snapshot/tree.json @@ -0,0 +1,168 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "ResourcePolicyTest-v2-ff": { + "id": "ResourcePolicyTest-v2-ff", + "path": "ResourcePolicyTest-v2-ff", + "children": { + "TableTestV2": { + "id": "TableTestV2", + "path": "ResourcePolicyTest-v2-ff/TableTestV2", + "children": { + "Resource": { + "id": "Resource", + "path": "ResourcePolicyTest-v2-ff/TableTestV2/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::DynamoDB::GlobalTable", + "aws:cdk:cloudformation:props": { + "attributeDefinitions": [ + { + "attributeName": "id", + "attributeType": "S" + } + ], + "billingMode": "PAY_PER_REQUEST", + "keySchema": [ + { + "attributeName": "id", + "keyType": "HASH" + } + ], + "replicas": [ + { + "region": "eu-west-1", + "resourcePolicy": { + "policyDocument": { + "Statement": [ + { + "Action": "dynamodb:*", + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:aws:iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + }, + "Resource": "*" + } + ], + "Version": "2012-10-17" + } + } + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_dynamodb.CfnGlobalTable", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_dynamodb.TableV2", + "version": "0.0.0" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "ResourcePolicyTest-v2-ff/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "ResourcePolicyTest-v2-ff/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + }, + "table-v2-feature-flag": { + "id": "table-v2-feature-flag", + "path": "table-v2-feature-flag", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "table-v2-feature-flag/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "table-v2-feature-flag/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "table-v2-feature-flag/DefaultTest/DeployAssert", + "children": { + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "table-v2-feature-flag/DefaultTest/DeployAssert/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "table-v2-feature-flag/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.3.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.App", + "version": "0.0.0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.featureflag.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.featureflag.ts index 72f79c087b138..fe260843e1d4d 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.featureflag.ts +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.featureflag.ts @@ -32,9 +32,6 @@ class TestStack extends Stack { }, removalPolicy: RemovalPolicy.DESTROY, resourcePolicy: docu, - replicas: [{ - region: 'eu-west-2', - }], }); } } From 973a3799a8a68e71678df084a8f7336023e8f6a3 Mon Sep 17 00:00:00 2001 From: Lee Date: Wed, 28 Aug 2024 15:44:24 +0100 Subject: [PATCH 7/7] Update features.ts to remove blank space --- packages/aws-cdk-lib/cx-api/lib/features.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/aws-cdk-lib/cx-api/lib/features.ts b/packages/aws-cdk-lib/cx-api/lib/features.ts index fb40431350fea..d6acfcecf146f 100644 --- a/packages/aws-cdk-lib/cx-api/lib/features.ts +++ b/packages/aws-cdk-lib/cx-api/lib/features.ts @@ -108,7 +108,6 @@ export const LOG_API_RESPONSE_DATA_PROPERTY_TRUE_DEFAULT = '@aws-cdk/custom-reso export const DYNAMODB_TABLEV2_RESOURCE_POLICY_PER_REPLICA = '@aws-cdk/aws-dynamodb:resourcePolicyPerReplica'; export const S3_KEEP_NOTIFICATION_IN_IMPORTED_BUCKET = '@aws-cdk/aws-s3:keepNotificationInImportedBucket'; - export const FLAGS: Record = { ////////////////////////////////////////////////////////////////////// [ENABLE_STACK_NAME_DUPLICATES_CONTEXT]: {