Skip to content

Commit 24d2adf

Browse files
authored
fix(dynamodb): resolve circular dependency with AccountRootPrincipal grants (#35983)
### Issue # (if applicable) Closes #35967. ### Reason for this change In CDK 2.222.0, PR #35554 fixed `addToResourcePolicy()` to actually work (it was previously a no-op). This exposed a circular dependency issue when using `grantReadData()` or other grant methods with `AccountRootPrincipal`. When `AccountRootPrincipal` is used with grant methods, the IAM Grant system adds the policy to the table's resource policy (since it's in the same account). The resource policy statement included the table's ARN (`!GetAtt Table.Arn`), creating a circular dependency: Table → ResourcePolicy → Table.Arn → Table. This is a regression that breaks existing user code that worked in 2.221.1. ### Description of changes Applied the established KMS grant pattern to DynamoDB by adding `resourceSelfArns: ['*']` parameter to `Grant.addToPrincipalOrResource()` calls in the `combinedGrant` method. **How it works:** - `resourceArns` contains actual table ARNs → used for **principal policies** (IAM user/role policies) - `resourceSelfArns: ['*']` → used for **resource policies** (table's resource policy) - IAM Grant system automatically chooses which to use based on context - No circular dependency because resource policy uses wildcard instead of `!GetAtt Table.Arn` **Why wildcard is safe:** - Wildcard is scoped to the table's resource policy (not global) - Resource policy is attached to specific table resource - Principal and Action fields still enforce access control - Same pattern used by KMS for years in production **Files modified:** - `packages/aws-cdk-lib/aws-dynamodb/lib/table.ts` - Added `resourceSelfArns: ['*']` to `combinedGrant` method - `packages/aws-cdk-lib/aws-dynamodb/lib/table-v2-base.ts` - Applied identical change for Table V2 - `packages/aws-cdk-lib/aws-dynamodb/README.md` - Added documentation about grant methods and resource policy interaction **Before (causes circular dependency):** ```typescript const table = new dynamodb.Table(this, 'Table', { partitionKey: { name: 'id', type: dynamodb.AttributeType.STRING }, }); // This caused circular dependency error in 2.222.0 table.grantReadData(new iam.AccountRootPrincipal()); // Error: Circular dependency between resources: [Table] ``` **After (no circular dependency):** ```typescript const table = new dynamodb.Table(this, 'Table', { partitionKey: { name: 'id', type: dynamodb.AttributeType.STRING }, }); // This now works correctly table.grantReadData(new iam.AccountRootPrincipal()); // ✓ Resource policy uses wildcard, no circular dependency ``` **CloudFormation template change:** ```json { "Resources": { "Table": { "Type": "AWS::DynamoDB::Table", "Properties": { "ResourcePolicy": { "PolicyDocument": { "Statement": [{ "Action": ["dynamodb:BatchGetItem", "dynamodb:GetItem", "dynamodb:Query", "dynamodb:Scan"], "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::ACCOUNT:root" }, "Resource": "*" }] } } } } } } ``` ### Describe any new or updated permissions being added N/A - This fix does not add new permissions. It resolves how existing grant methods generate resource policies to avoid circular dependencies. ### Description of how you validated changes - **Unit tests**: Added 2 new tests validating `AccountRootPrincipal` with grant methods - `packages/aws-cdk-lib/aws-dynamodb/test/dynamodb.test.ts`: Test for Table V1 - `packages/aws-cdk-lib/aws-dynamodb/test/table-v2.test.ts`: Test for Table V2 - Both tests verify resource policy uses wildcard (`*`) to avoid circular dependency - All 348 unit tests pass (346 existing + 2 new) - **Integration tests**: Enhanced existing integration test with grant scenario - `packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.add-to-resource-policy.ts` - Added TEST 3: Validates `grantWriteData(new AccountRootPrincipal())` works without circular dependency - Successfully deployed to AWS (us-east-1) - CloudFormation synthesis succeeds, no circular dependency errors - Snapshots updated with GrantTable resource - **Regression testing**: All 346 existing tests pass - Grant methods with IAM Users still work - Grant methods with IAM Roles still work - Grant methods with Service Principals still work - Tables with indexes work correctly - Global tables (Table V2) work correctly - Encrypted tables work correctly ### 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*
1 parent 771ea13 commit 24d2adf

File tree

10 files changed

+263
-15
lines changed

10 files changed

+263
-15
lines changed

packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.add-to-resource-policy.js.snapshot/add-to-resource-policy-test-stack.assets.json

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.add-to-resource-policy.js.snapshot/add-to-resource-policy-test-stack.template.json

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,65 @@
115115
},
116116
"UpdateReplacePolicy": "Delete",
117117
"DeletionPolicy": "Delete"
118+
},
119+
"GrantTable13160A8B": {
120+
"Type": "AWS::DynamoDB::Table",
121+
"Properties": {
122+
"AttributeDefinitions": [
123+
{
124+
"AttributeName": "id",
125+
"AttributeType": "S"
126+
}
127+
],
128+
"KeySchema": [
129+
{
130+
"AttributeName": "id",
131+
"KeyType": "HASH"
132+
}
133+
],
134+
"ProvisionedThroughput": {
135+
"ReadCapacityUnits": 5,
136+
"WriteCapacityUnits": 5
137+
},
138+
"ResourcePolicy": {
139+
"PolicyDocument": {
140+
"Statement": [
141+
{
142+
"Action": [
143+
"dynamodb:BatchWriteItem",
144+
"dynamodb:DeleteItem",
145+
"dynamodb:DescribeTable",
146+
"dynamodb:PutItem",
147+
"dynamodb:UpdateItem"
148+
],
149+
"Effect": "Allow",
150+
"Principal": {
151+
"AWS": {
152+
"Fn::Join": [
153+
"",
154+
[
155+
"arn:",
156+
{
157+
"Ref": "AWS::Partition"
158+
},
159+
":iam::",
160+
{
161+
"Ref": "AWS::AccountId"
162+
},
163+
":root"
164+
]
165+
]
166+
}
167+
},
168+
"Resource": "*"
169+
}
170+
],
171+
"Version": "2012-10-17"
172+
}
173+
}
174+
},
175+
"UpdateReplacePolicy": "Delete",
176+
"DeletionPolicy": "Delete"
118177
}
119178
},
120179
"Parameters": {

packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.add-to-resource-policy.js.snapshot/manifest.json

Lines changed: 65 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.add-to-resource-policy.js.snapshot/tree.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.add-to-resource-policy.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import { IntegTest } from '@aws-cdk/integ-tests-alpha';
2626
export class TestStack extends Stack {
2727
public readonly wildcardTable: dynamodb.Table;
2828
public readonly scopedTable: dynamodb.Table;
29+
public readonly grantTable: dynamodb.Table;
2930

3031
constructor(scope: Construct, id: string, props?: StackProps) {
3132
super(scope, id, props);
@@ -66,6 +67,22 @@ export class TestStack extends Stack {
6667
// Use CloudFormation intrinsic function to construct table ARN with known table name
6768
resources: [Fn.sub('arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/my-explicit-scoped-table')],
6869
}));
70+
71+
// TEST 3: Table using grant methods with AccountRootPrincipal
72+
// This validates the fix for issue #35967: circular dependency when using grant methods
73+
// Before fix: grant methods with AccountRootPrincipal caused circular dependency
74+
// After fix: grant methods use resourceSelfArns: ['*'] to avoid circular dependency
75+
this.grantTable = new dynamodb.Table(this, 'GrantTable', {
76+
partitionKey: {
77+
name: 'id',
78+
type: dynamodb.AttributeType.STRING,
79+
},
80+
removalPolicy: RemovalPolicy.DESTROY,
81+
});
82+
83+
// This should NOT cause circular dependency - validates fix for #35967
84+
// Using grantWriteData because it has simpler actions valid for resource policies
85+
this.grantTable.grantWriteData(new iam.AccountRootPrincipal());
6986
}
7087
}
7188

0 commit comments

Comments
 (0)