From 0b03eb0f62c132c1bd586a8ec31818398d07707f Mon Sep 17 00:00:00 2001 From: Lee Date: Wed, 23 Oct 2024 19:36:06 +0100 Subject: [PATCH] fix(dynamodb): replication regions are incompatible with resource policies in TableV2 and feature flag (#31513) ### Issue # (if applicable) Closes #30705 ### Reason for this change Resource policies were shared across all replicas in a region. ### Description of changes Changed the logic to only apply resource policy to the local replica region, or to specific replicas only when defined. ### Description of how you validated changes yes ### Checklist - [X] My code adheres to the [CONTRIBUTING GUIDE](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) and [DESIGN GUIDELINES](https://github.com/aws/aws-cdk/blob/main/docs/DESIGN_GUIDELINES.md) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../ResourcePolicyTest-v2-FF.assets.json | 20 ++ .../ResourcePolicyTest-v2-FF.template.json | 95 ++++++++++ .../cdk.out | 1 + .../integ.json | 12 ++ .../manifest.json | 115 ++++++++++++ ...efaultTestDeployAssertBE3353C7.assets.json | 19 ++ ...aultTestDeployAssertBE3353C7.template.json | 36 ++++ .../tree.json | 174 ++++++++++++++++++ .../test/integ.dynamodb-v2.policy-ff.ts | 47 +++++ .../test/integ.dynamodb-v2.policy.ts | 9 +- .../aws-cdk-lib/aws-dynamodb/lib/table-v2.ts | 12 +- packages/aws-cdk-lib/cx-api/README.md | 19 ++ packages/aws-cdk-lib/cx-api/lib/features.ts | 16 ++ 13 files changed, 570 insertions(+), 5 deletions(-) create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy-ff.js.snapshot/ResourcePolicyTest-v2-FF.assets.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy-ff.js.snapshot/ResourcePolicyTest-v2-FF.template.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy-ff.js.snapshot/cdk.out create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy-ff.js.snapshot/integ.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy-ff.js.snapshot/manifest.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy-ff.js.snapshot/tablev2resourcepolicyintegtestDefaultTestDeployAssertBE3353C7.assets.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy-ff.js.snapshot/tablev2resourcepolicyintegtestDefaultTestDeployAssertBE3353C7.template.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy-ff.js.snapshot/tree.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy-ff.ts diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy-ff.js.snapshot/ResourcePolicyTest-v2-FF.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy-ff.js.snapshot/ResourcePolicyTest-v2-FF.assets.json new file mode 100644 index 0000000000000..31c84ba046a09 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy-ff.js.snapshot/ResourcePolicyTest-v2-FF.assets.json @@ -0,0 +1,20 @@ +{ + "version": "38.0.1", + "files": { + "0aaa25e74c3bd8b4c72922081490518294b93ff15f40df38ae16908cbe6d69c5": { + "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": "0aaa25e74c3bd8b4c72922081490518294b93ff15f40df38ae16908cbe6d69c5.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.policy-ff.js.snapshot/ResourcePolicyTest-v2-FF.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy-ff.js.snapshot/ResourcePolicyTest-v2-FF.template.json new file mode 100644 index 0000000000000..28240d04c34e3 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy-ff.js.snapshot/ResourcePolicyTest-v2-FF.template.json @@ -0,0 +1,95 @@ +{ + "Resources": { + "TableTestV215EEA02B7": { + "Type": "AWS::DynamoDB::GlobalTable", + "Properties": { + "AttributeDefinitions": [ + { + "AttributeName": "id", + "AttributeType": "S" + } + ], + "BillingMode": "PAY_PER_REQUEST", + "KeySchema": [ + { + "AttributeName": "id", + "KeyType": "HASH" + } + ], + "Replicas": [ + { + "Region": "eu-west-2" + }, + { + "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" + } + } + } + ], + "StreamSpecification": { + "StreamViewType": "NEW_AND_OLD_IMAGES" + } + }, + "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.policy-ff.js.snapshot/cdk.out b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy-ff.js.snapshot/cdk.out new file mode 100644 index 0000000000000..c6e612584e352 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy-ff.js.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"38.0.1"} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy-ff.js.snapshot/integ.json b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy-ff.js.snapshot/integ.json new file mode 100644 index 0000000000000..0be4870b3749d --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy-ff.js.snapshot/integ.json @@ -0,0 +1,12 @@ +{ + "version": "38.0.1", + "testCases": { + "table-v2-resource-policy-integ-test/DefaultTest": { + "stacks": [ + "ResourcePolicyTest-v2-FF" + ], + "assertionStack": "table-v2-resource-policy-integ-test/DefaultTest/DeployAssert", + "assertionStackName": "tablev2resourcepolicyintegtestDefaultTestDeployAssertBE3353C7" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy-ff.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy-ff.js.snapshot/manifest.json new file mode 100644 index 0000000000000..a84913e0473df --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy-ff.js.snapshot/manifest.json @@ -0,0 +1,115 @@ +{ + "version": "38.0.1", + "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, + "notificationArns": [], + "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/0aaa25e74c3bd8b4c72922081490518294b93ff15f40df38ae16908cbe6d69c5.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-1/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "TableTestV215EEA02B7" + } + ], + "/ResourcePolicyTest-v2-FF/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/ResourcePolicyTest-v2-FF/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "ResourcePolicyTest-v2-FF" + }, + "tablev2resourcepolicyintegtestDefaultTestDeployAssertBE3353C7.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "tablev2resourcepolicyintegtestDefaultTestDeployAssertBE3353C7.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "tablev2resourcepolicyintegtestDefaultTestDeployAssertBE3353C7": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "tablev2resourcepolicyintegtestDefaultTestDeployAssertBE3353C7.template.json", + "terminationProtection": false, + "validateOnSynth": false, + "notificationArns": [], + "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": [ + "tablev2resourcepolicyintegtestDefaultTestDeployAssertBE3353C7.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": [ + "tablev2resourcepolicyintegtestDefaultTestDeployAssertBE3353C7.assets" + ], + "metadata": { + "/table-v2-resource-policy-integ-test/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/table-v2-resource-policy-integ-test/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "table-v2-resource-policy-integ-test/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.policy-ff.js.snapshot/tablev2resourcepolicyintegtestDefaultTestDeployAssertBE3353C7.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy-ff.js.snapshot/tablev2resourcepolicyintegtestDefaultTestDeployAssertBE3353C7.assets.json new file mode 100644 index 0000000000000..d49a26b8c2ab8 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy-ff.js.snapshot/tablev2resourcepolicyintegtestDefaultTestDeployAssertBE3353C7.assets.json @@ -0,0 +1,19 @@ +{ + "version": "38.0.1", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "tablev2resourcepolicyintegtestDefaultTestDeployAssertBE3353C7.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.policy-ff.js.snapshot/tablev2resourcepolicyintegtestDefaultTestDeployAssertBE3353C7.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy-ff.js.snapshot/tablev2resourcepolicyintegtestDefaultTestDeployAssertBE3353C7.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.policy-ff.js.snapshot/tablev2resourcepolicyintegtestDefaultTestDeployAssertBE3353C7.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.policy-ff.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy-ff.js.snapshot/tree.json new file mode 100644 index 0000000000000..b3a565db2d3c0 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy-ff.js.snapshot/tree.json @@ -0,0 +1,174 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "ResourcePolicyTest-v2-FF": { + "id": "ResourcePolicyTest-v2-FF", + "path": "ResourcePolicyTest-v2-FF", + "children": { + "TableTestV2-1": { + "id": "TableTestV2-1", + "path": "ResourcePolicyTest-v2-FF/TableTestV2-1", + "children": { + "Resource": { + "id": "Resource", + "path": "ResourcePolicyTest-v2-FF/TableTestV2-1/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-2" + }, + { + "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" + } + } + } + ], + "streamSpecification": { + "streamViewType": "NEW_AND_OLD_IMAGES" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_dynamodb.CfnGlobalTable", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_dynamodb.TableBaseV2", + "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-resource-policy-integ-test": { + "id": "table-v2-resource-policy-integ-test", + "path": "table-v2-resource-policy-integ-test", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "table-v2-resource-policy-integ-test/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "table-v2-resource-policy-integ-test/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "table-v2-resource-policy-integ-test/DefaultTest/DeployAssert", + "children": { + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "table-v2-resource-policy-integ-test/DefaultTest/DeployAssert/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "table-v2-resource-policy-integ-test/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.policy-ff.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy-ff.ts new file mode 100644 index 0000000000000..819561ce50d08 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy-ff.ts @@ -0,0 +1,47 @@ +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-1', { + 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-resource-policy-integ-test', { + testCases: [stack], +}); \ No newline at end of file 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..50ca86a684b2d 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 @@ -4,7 +4,11 @@ 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(); +const app = new App({ + postCliContext: { + '@aws-cdk/aws-dynamodb:resourcePolicyPerReplica': false, + }, +}); class TestStack extends Stack { constructor(scope: Construct, id: string, props?: StackProps) { @@ -21,7 +25,7 @@ class TestStack extends Stack { }); // table with resource policy - const table = new dynamodb.TableV2(this, 'TableTestV2-1', { + new dynamodb.TableV2(this, 'TableTestV2-1', { partitionKey: { name: 'id', type: dynamodb.AttributeType.STRING, @@ -30,7 +34,6 @@ class TestStack extends Stack { resourcePolicy: docu, }); - table.grantReadData(new iam.AccountPrincipal('123456789012')); } } 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..b6507d3f7deb1 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 '../../cx-api'; const HASH_KEY_TYPE = 'HASH'; const RANGE_KEY_TYPE = 'RANGE'; @@ -664,7 +665,14 @@ 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; + /* + * 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, diff --git a/packages/aws-cdk-lib/cx-api/README.md b/packages/aws-cdk-lib/cx-api/README.md index 68a16be402040..f90bcac9b4f13 100644 --- a/packages/aws-cdk-lib/cx-api/README.md +++ b/packages/aws-cdk-lib/cx-api/README.md @@ -475,3 +475,22 @@ _cdk.json_ } } ``` + +* `@aws-cdk/aws-dynamodb:resourcePolicyPerReplica` + +If this flag is not set, the default behavior for \`TableV2\` is to use a different \`resourcePolicy\` for each replica. + +If this flag is set to false, the behavior is that each replica shares the same \`resourcePolicy\` as the source table. +This will prevent you from creating a new table which has an additional replica and a resource policy. + +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": false, + }, +} +``` \ 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 7cd1ee4b5e7e0..182d14a5f738f 100644 --- a/packages/aws-cdk-lib/cx-api/lib/features.ts +++ b/packages/aws-cdk-lib/cx-api/lib/features.ts @@ -108,6 +108,7 @@ export const LOG_API_RESPONSE_DATA_PROPERTY_TRUE_DEFAULT = '@aws-cdk/custom-reso export const S3_KEEP_NOTIFICATION_IN_IMPORTED_BUCKET = '@aws-cdk/aws-s3:keepNotificationInImportedBucket'; export const USE_NEW_S3URI_PARAMETERS_FOR_BEDROCK_INVOKE_MODEL_TASK = '@aws-cdk/aws-stepfunctions-tasks:useNewS3UriParametersForBedrockInvokeModelTask'; export const REDUCE_EC2_FARGATE_CLOUDWATCH_PERMISSIONS = '@aws-cdk/aws-ecs:reduceEc2FargateCloudWatchPermissions'; +export const DYNAMODB_TABLEV2_RESOURCE_POLICY_PER_REPLICA = '@aws-cdk/aws-dynamodb:resourcePolicyPerReplica'; export const EC2_SUM_TIMEOUT_ENABLED = '@aws-cdk/aws-ec2:ec2SumTImeoutEnabled'; export const APPSYNC_GRAPHQLAPI_SCOPE_LAMBDA_FUNCTION_PERMISSION = '@aws-cdk/aws-appsync:appSyncGraphQLAPIScopeLambdaPermission'; export const USE_CORRECT_VALUE_FOR_INSTANCE_RESOURCE_ID_PROPERTY = '@aws-cdk/aws-rds:setCorrectValueForDatabaseInstanceReadReplicaInstanceResourceId'; @@ -1149,6 +1150,21 @@ export const FLAGS: Record = { compatibilityWithOldBehaviorMd: 'Disable the feature flag to continue grant permissions to log group when no log group is specified', }, + ////////////////////////////////////////////////////////////////////// + [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 a different \`resourcePolicy\` for each replica. + + If this flag is set to false, the behavior is that each replica shares the same \`resourcePolicy\` as the source table. + This will prevent you from creating a new table which has an additional replica and a resource policy. + + 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, + }, + ////////////////////////////////////////////////////////////////////// [EC2_SUM_TIMEOUT_ENABLED]: { type: FlagType.BugFix,