konokenj.cdk-api-mcp-server 0.53.0__py3-none-any.whl → 0.55.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of konokenj.cdk-api-mcp-server might be problematic. Click here for more details.

Files changed (40) hide show
  1. cdk_api_mcp_server/__about__.py +1 -1
  2. cdk_api_mcp_server/resources/aws-cdk/constructs/@aws-cdk/aws-eks-v2-alpha/README.md +45 -45
  3. cdk_api_mcp_server/resources/aws-cdk/constructs/@aws-cdk/aws-imagebuilder-alpha/README.md +298 -0
  4. cdk_api_mcp_server/resources/aws-cdk/constructs/@aws-cdk/aws-sagemaker-alpha/README.md +32 -0
  5. cdk_api_mcp_server/resources/aws-cdk/constructs/@aws-cdk/mixins-preview/README.md +167 -5
  6. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/README.md/README.md +2 -0
  7. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-apigateway/README.md +25 -0
  8. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-apigateway/integ.lambda-permission-consolidation.ts +55 -0
  9. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-apigatewayv2-integrations/README.md +35 -0
  10. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-apigatewayv2-integrations/integ.lambda-permission-consolidation.ts +45 -0
  11. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-cognito/README.md +2 -2
  12. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-dynamodb/README.md +26 -0
  13. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-dynamodb/integ.dynamodb.add-to-resource-policy.ts +17 -0
  14. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-ecs/integ.placement-strategies.ts +32 -8
  15. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-eks/README.md +86 -86
  16. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-eks/integ.eks-al2023-nodegroup.ts +1 -1
  17. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-eks/integ.fargate-cluster.ts +1 -1
  18. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-lambda/integ.runtime.inlinecode.ts +7 -0
  19. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-opensearchservice/integ.opensearch.ebs.ts +1 -1
  20. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-rds/README.md +1 -1
  21. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-rds/integ.cluster-cloudwatch-logs-exports.ts +56 -0
  22. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-route53/README.md +32 -31
  23. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-route53/integ.zone-delegation-iam-stack.ts +66 -0
  24. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-s3-deployment/integ.bucket-deployment-big-response.ts +4 -0
  25. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-s3-deployment/integ.bucket-deployment-data.ts +15 -0
  26. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-s3-deployment/integ.bucket-deployment-large-file.ts +3 -0
  27. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-secretsmanager/integ.secret.dynamic-reference-key.ts +38 -0
  28. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-stepfunctions-tasks/README.md +14 -3
  29. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-stepfunctions-tasks/integ.evaluate-expression-arm64.ts +27 -0
  30. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-stepfunctions-tasks/integ.evaluate-expression-default.ts +25 -0
  31. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-stepfunctions-tasks/integ.evaluate-expression-mixed-arch.ts +35 -0
  32. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-stepfunctions-tasks/integ.evaluate-expression-x86.ts +27 -0
  33. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/custom-resources/README.md +56 -0
  34. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/custom-resources/integ.external-id.ts +80 -0
  35. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/interfaces/README.md +33 -0
  36. {konokenj_cdk_api_mcp_server-0.53.0.dist-info → konokenj_cdk_api_mcp_server-0.55.0.dist-info}/METADATA +2 -2
  37. {konokenj_cdk_api_mcp_server-0.53.0.dist-info → konokenj_cdk_api_mcp_server-0.55.0.dist-info}/RECORD +40 -28
  38. {konokenj_cdk_api_mcp_server-0.53.0.dist-info → konokenj_cdk_api_mcp_server-0.55.0.dist-info}/WHEEL +0 -0
  39. {konokenj_cdk_api_mcp_server-0.53.0.dist-info → konokenj_cdk_api_mcp_server-0.55.0.dist-info}/entry_points.txt +0 -0
  40. {konokenj_cdk_api_mcp_server-0.53.0.dist-info → konokenj_cdk_api_mcp_server-0.55.0.dist-info}/licenses/LICENSE.txt +0 -0
@@ -1,16 +1,178 @@
1
- # CDK Mixins: Composable Abstractions for AWS Resources
2
-
1
+ # CDK Mixins (Preview)
3
2
  <!--BEGIN STABILITY BANNER-->
4
3
 
5
4
  ---
6
5
 
7
6
  ![cdk-constructs: Experimental](https://img.shields.io/badge/cdk--constructs-experimental-important.svg?style=for-the-badge)
8
7
 
9
- > The APIs of higher level constructs in this module are experimental and under active development. They are subject to non-backward compatible changes or removal in any future version. These are not subject to the [Semantic Versioning](https://semver.org/) model and breaking changes will be announced in the release notes. This means that while you may use them, you may need to update your source code when upgrading to a newer version of this package.
8
+ > The APIs of higher level constructs in this module are experimental and under active development.
9
+ > They are subject to non-backward compatible changes or removal in any future version. These are
10
+ > not subject to the [Semantic Versioning](https://semver.org/) model and breaking changes will be
11
+ > announced in the release notes. This means that while you may use them, you may need to update
12
+ > your source code when upgrading to a newer version of this package.
10
13
 
11
14
  ---
12
15
 
13
16
  <!--END STABILITY BANNER-->
14
17
 
15
- Implementation of the CDK Mixins proposal.
16
- See <https://github.com/aws/aws-cdk-rfcs/pull/824> for details.
18
+ CDK Mixins provide a new, advanced way to add functionality through composable abstractions.
19
+ Unlike traditional L2 constructs that bundle all features together, Mixins allow you to pick and choose exactly the capabilities you need for constructs.
20
+
21
+ ## Key Benefits
22
+
23
+ * **Universal Compatibility**: Apply the same abstractions to L1 constructs, L2 constructs, or custom constructs
24
+ * **Composable Design**: Mix and match features without being locked into specific implementations
25
+ * **Cross-Service Abstractions**: Use common patterns like encryption across different AWS services
26
+ * **Escape Hatch Freedom**: Customize resources in a safe, typed way while keeping the abstractions you want
27
+
28
+ ## Basic Usage
29
+
30
+ Mixins use `Mixins.of()` as the fundamental API for applying abstractions to constructs:
31
+
32
+ ```typescript
33
+ // Base form: apply mixins to any construct
34
+ const bucket = new s3.CfnBucket(scope, "MyBucket");
35
+ Mixins.of(bucket)
36
+ .apply(new EncryptionAtRest())
37
+ .apply(new AutoDeleteObjects());
38
+ ```
39
+
40
+ ### Fluent Syntax with `.with()`
41
+
42
+ For convenience, you can use the `.with()` method for a more fluent syntax:
43
+
44
+ ```typescript
45
+ import '@aws-cdk/mixins-preview/with';
46
+
47
+ const bucket = new s3.CfnBucket(scope, "MyBucket")
48
+ .with(new EnableVersioning())
49
+ .with(new AutoDeleteObjects());
50
+ ```
51
+
52
+ The `.with()` method is available after importing `@aws-cdk/mixins-preview/with`, which augments all constructs with this method. It provides the same functionality as `Mixins.of().apply()` but with a more chainable API.
53
+
54
+ > **Note**: The `.with()` fluent syntax is only available in JavaScript and TypeScript. Other jsii languages (Python, Java, C#, and Go) should use the `Mixins.of(...).mustApply()` syntax instead. The import requirement is temporary during the preview phase. Once the API is stable, the `.with()` method will be available by default on all constructs and in all languages.
55
+
56
+ ## Creating Custom Mixins
57
+
58
+ Mixins are simple classes that implement the `IMixin` interface:
59
+
60
+ ```typescript
61
+ // Simple mixin that enables versioning
62
+ class CustomVersioningMixin implements IMixin {
63
+ supports(construct: any): boolean {
64
+ return construct instanceof s3.CfnBucket;
65
+ }
66
+
67
+ applyTo(bucket: any): any {
68
+ bucket.versioningConfiguration = {
69
+ status: "Enabled"
70
+ };
71
+ return bucket;
72
+ }
73
+ }
74
+
75
+ // Usage
76
+ const bucket = new s3.CfnBucket(scope, "MyBucket");
77
+ Mixins.of(bucket).apply(new CustomVersioningMixin());
78
+ ```
79
+
80
+ ## Construct Selection
81
+
82
+ Mixins operate on construct trees and can be applied selectively:
83
+
84
+ ```typescript
85
+ // Apply to all constructs in a scope
86
+ Mixins.of(scope).apply(new EncryptionAtRest());
87
+
88
+ // Apply to specific resource types
89
+ Mixins.of(scope, ConstructSelector.resourcesOfType(s3.CfnBucket))
90
+ .apply(new EncryptionAtRest());
91
+
92
+ // Apply to constructs matching a pattern
93
+ Mixins.of(scope, ConstructSelector.byId(/.*-prod-.*/))
94
+ .apply(new ProductionSecurityMixin());
95
+ ```
96
+
97
+ ## Built-in Mixins
98
+
99
+ ### Cross-Service Mixins
100
+
101
+ **EncryptionAtRest**: Applies encryption to supported AWS resources
102
+
103
+ ```typescript
104
+ // Works across different resource types
105
+ const bucket = new s3.CfnBucket(scope, "Bucket");
106
+ Mixins.of(bucket).apply(new EncryptionAtRest());
107
+
108
+ const logGroup = new logs.CfnLogGroup(scope, "LogGroup");
109
+ Mixins.of(logGroup).apply(new EncryptionAtRest());
110
+ ```
111
+
112
+ ### S3-Specific Mixins
113
+
114
+ **AutoDeleteObjects**: Configures automatic object deletion for S3 buckets
115
+
116
+ ```typescript
117
+ const bucket = new s3.CfnBucket(scope, "Bucket");
118
+ Mixins.of(bucket).apply(new AutoDeleteObjects());
119
+ ```
120
+
121
+ **EnableVersioning**: Enables versioning on S3 buckets
122
+
123
+ ```typescript
124
+ const bucket = new s3.CfnBucket(scope, "Bucket");
125
+ Mixins.of(bucket).apply(new EnableVersioning());
126
+ ```
127
+
128
+ ### Generic Mixins
129
+
130
+ **CfnPropertiesMixin**: Applies arbitrary CloudFormation properties
131
+
132
+ ```typescript
133
+ const bucket = new s3.CfnBucket(scope, "Bucket");
134
+ Mixins.of(bucket).apply(new CfnPropertiesMixin({
135
+ customProperty: { enabled: true }
136
+ }));
137
+ ```
138
+
139
+ ## Error Handling
140
+
141
+ Mixins provide comprehensive error handling:
142
+
143
+ ```typescript
144
+ // Graceful handling of unsupported constructs
145
+ Mixins.of(scope)
146
+ .apply(new EncryptionAtRest()); // Skips unsupported constructs
147
+
148
+ // Strict application that requires all constructs to match
149
+ Mixins.of(scope)
150
+ .mustApply(new EncryptionAtRest()); // Throws if no constructs support the mixin
151
+ ```
152
+
153
+ ## API Reference
154
+
155
+ ### Core Interfaces
156
+
157
+ * `IMixin` - Interface that all mixins must implement
158
+ * `Mixins` - Main entry point for applying mixins
159
+ * `ConstructSelector` - Selects constructs from a tree based on criteria
160
+ * `MixinApplicator` - Applies mixins to selected constructs
161
+
162
+ ### Mixins
163
+
164
+ * `EncryptionAtRest` - Cross-service encryption mixin
165
+ * `AutoDeleteObjects` - S3 auto-delete objects mixin
166
+ * `EnableVersioning` - S3 versioning mixin
167
+ * `CfnPropertiesMixin` - Generic CloudFormation properties mixin
168
+
169
+ ### Selectors
170
+
171
+ * `ConstructSelector.all()` - Select all constructs
172
+ * `ConstructSelector.cfnResource()` - Select CfnResource constructs
173
+ * `ConstructSelector.resourcesOfType()` - Select by type
174
+ * `ConstructSelector.byId()` - Select by ID pattern
175
+
176
+ ## License
177
+
178
+ This project is licensed under the Apache-2.0 License.
@@ -480,6 +480,8 @@ CloudFormation to re-read the secret.
480
480
  `SecretValue.ssmSecure()` is only supported for a limited set of resources.
481
481
  [Click here for a list of supported resources and properties](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/dynamic-references.html#template-parameters-dynamic-patterns-resources).
482
482
 
483
+ `SecretValue.cfnDynamicReferenceKey` takes the same parameters as `SecretValue.secretsManager` and returns a key which can be used within a [dynamic reference](#dynamic-references) to dynamically load a secret from AWS Secrets Manager.
484
+
483
485
  ## ARN manipulation
484
486
 
485
487
  Sometimes you will need to put together or pick apart Amazon Resource Names
@@ -334,6 +334,31 @@ const getMessageIntegration = new apigateway.AwsIntegration({
334
334
  });
335
335
  ```
336
336
 
337
+ ### Lambda Integration Permissions
338
+
339
+ By default, creating a `LambdaIntegration` will add a permission for API Gateway to invoke your AWS Lambda function, scoped to the specific method which uses the integration.
340
+
341
+ If you reuse the same AWS Lambda function for many integrations, the AWS Lambda permission policy size can be exceeded by adding a separate policy statement for each method which invokes the AWS Lambda function. To avoid this, you can opt to scope permissions to any method on the API by setting `scopePermissionToMethod` to `false`, and this will ensure only a single policy statement is added to the AWS Lambda permission policy.
342
+
343
+ ```ts
344
+ declare const book: apigateway.Resource;
345
+ declare const backend: lambda.Function;
346
+
347
+ const getBookIntegration = new apigateway.LambdaIntegration(backend, {
348
+ scopePermissionToMethod: false,
349
+ });
350
+ const createBookIntegration = new apigateway.LambdaIntegration(backend, {
351
+ scopePermissionToMethod: false,
352
+ });
353
+
354
+ book.addMethod('GET', getBookIntegration);
355
+ book.addMethod('POST', createBookIntegration);
356
+ ```
357
+
358
+ In the above example, a single permission is added, shared by both `getBookIntegration` and `createBookIntegration`.
359
+
360
+ Note that setting `scopePermissionToMethod` to `false` will always allow test invocations, no matter the value specified for `allowTestInvoke`.
361
+
337
362
  ## Usage Plan & API Keys
338
363
 
339
364
  A usage plan specifies who can access one or more deployed API stages and methods, and the rate at which they can be
@@ -0,0 +1,55 @@
1
+ import { Code, Function, Runtime } from 'aws-cdk-lib/aws-lambda';
2
+ import { App, Stack } from 'aws-cdk-lib';
3
+ import { ExpectedResult, IntegTest } from '@aws-cdk/integ-tests-alpha';
4
+ import { Construct } from 'constructs';
5
+ import { LambdaIntegration, RestApi } from 'aws-cdk-lib/aws-apigateway';
6
+
7
+ class LambdaPermissionConsolidationStack extends Stack {
8
+ public readonly api: RestApi;
9
+ constructor(scope: Construct) {
10
+ super(scope, 'LambdaPermissionConsolidationStack');
11
+
12
+ const fn = new Function(this, 'Handler', {
13
+ code: Code.fromInline(`exports.handler = async function(event) {
14
+ return {
15
+ body: JSON.stringify({
16
+ message: 'Hello from ' + event.httpMethod,
17
+ }),
18
+ statusCode: 200,
19
+ headers: { 'Content-Type': 'application/json' }
20
+ };
21
+ }`),
22
+ runtime: Runtime.NODEJS_18_X,
23
+ handler: 'index.handler',
24
+ });
25
+
26
+ this.api = new RestApi(this, 'Api', {
27
+ cloudWatchRole: true,
28
+ });
29
+
30
+ const methods = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD'];
31
+ methods.forEach(method => {
32
+ this.api.root.addMethod(method, new LambdaIntegration(fn, {
33
+ scopePermissionToMethod: false,
34
+ }));
35
+ });
36
+ }
37
+ }
38
+
39
+ const app = new App({
40
+ postCliContext: {
41
+ '@aws-cdk/aws-lambda:useCdkManagedLogGroup': false,
42
+ },
43
+ });
44
+ const testCase = new LambdaPermissionConsolidationStack(app);
45
+ const integ = new IntegTest(app, 'lambda-permission-consolidation', {
46
+ testCases: [testCase],
47
+ });
48
+
49
+ // Test that all methods work after consolidation
50
+ const call = integ.assertions.httpApiCall(testCase.api.deploymentStage.urlForPath('/'), {
51
+ method: 'GET',
52
+ });
53
+ call.expect(ExpectedResult.objectLike({
54
+ body: { message: 'Hello from GET' },
55
+ }));
@@ -47,6 +47,41 @@ httpApi.addRoutes({
47
47
  });
48
48
  ```
49
49
 
50
+ #### Lambda Integration Permissions
51
+
52
+ By default, creating a `HttpLambdaIntegration` will add a permission for API Gateway to invoke your AWS Lambda function, scoped to the specific route which uses the integration.
53
+
54
+ If you reuse the same AWS Lambda function for many integrations, the AWS Lambda permission policy size can be exceeded by adding a separate policy statement for each route which invokes the AWS Lambda function. To avoid this, you can opt to scope permissions to any route on the API by setting `scopePermissionToRoute` to `false`, and this will ensure only a single policy statement is added to the AWS Lambda permission policy.
55
+
56
+ ```ts
57
+ import { HttpLambdaIntegration } from 'aws-cdk-lib/aws-apigatewayv2-integrations';
58
+
59
+ declare const booksDefaultFn: lambda.Function;
60
+
61
+ const httpApi = new apigwv2.HttpApi(this, 'HttpApi');
62
+
63
+ const getBooksIntegration = new HttpLambdaIntegration('GetBooksIntegration', booksDefaultFn, {
64
+ scopePermissionToRoute: false,
65
+ });
66
+ const createBookIntegration = new HttpLambdaIntegration('CreateBookIntegration', booksDefaultFn, {
67
+ scopePermissionToRoute: false,
68
+ });
69
+
70
+ httpApi.addRoutes({
71
+ path: '/books',
72
+ methods: [ apigwv2.HttpMethod.GET ],
73
+ integration: getBooksIntegration,
74
+ });
75
+
76
+ httpApi.addRoutes({
77
+ path: '/books',
78
+ methods: [ apigwv2.HttpMethod.POST ],
79
+ integration: createBookIntegration,
80
+ });
81
+ ```
82
+
83
+ In the above example, a single permission is added, shared by both `getBookIntegration` and `createBookIntegration`.
84
+
50
85
  ### HTTP Proxy
51
86
 
52
87
  HTTP Proxy integrations enables connecting an HTTP API route to a publicly routable HTTP endpoint. When a client
@@ -0,0 +1,45 @@
1
+ import { HttpApi, HttpMethod, HttpRoute, HttpRouteKey } from 'aws-cdk-lib/aws-apigatewayv2';
2
+ import * as lambda from 'aws-cdk-lib/aws-lambda';
3
+ import { ExpectedResult, IntegTest } from '@aws-cdk/integ-tests-alpha';
4
+ import { App, Stack } from 'aws-cdk-lib';
5
+ import { HttpLambdaIntegration } from 'aws-cdk-lib/aws-apigatewayv2-integrations';
6
+
7
+ const app = new App({
8
+ postCliContext: {
9
+ '@aws-cdk/aws-lambda:useCdkManagedLogGroup': false,
10
+ },
11
+ });
12
+ const stack = new Stack(app, 'integ-lambda-permission-consolidation');
13
+
14
+ const httpApi = new HttpApi(stack, 'HttpApi');
15
+
16
+ const lambdaHandler = new lambda.Function(stack, 'Handler', {
17
+ runtime: lambda.Runtime.NODEJS_18_X,
18
+ handler: 'index.handler',
19
+ code: new lambda.InlineCode('exports.handler = async function(event, context) { return { statusCode: 200, body: JSON.stringify({ message: \'Hello from \' + event.requestContext.http.path }) }; };'),
20
+ });
21
+
22
+ // Add several routes
23
+ for (let i = 1; i <= 10; i++) {
24
+ new HttpRoute(stack, `Route${i}`, {
25
+ httpApi: httpApi,
26
+ integration: new HttpLambdaIntegration(`Integration${i}`, lambdaHandler, {
27
+ scopePermissionToRoute: false,
28
+ }),
29
+ routeKey: HttpRouteKey.with(`/path${i}`, HttpMethod.GET),
30
+ });
31
+ }
32
+
33
+ // Integ Test Assertions
34
+ const integ = new IntegTest(app, 'Integ', { testCases: [stack] });
35
+
36
+ // Test that routes work after consolidation
37
+ integ.assertions.httpApiCall(httpApi.apiEndpoint + '/path1').expect(ExpectedResult.objectLike({
38
+ body: { message: 'Hello from /path1' },
39
+ status: 200,
40
+ }));
41
+
42
+ integ.assertions.httpApiCall(httpApi.apiEndpoint + '/path12').expect(ExpectedResult.objectLike({
43
+ body: { message: 'Hello from /path10' },
44
+ status: 200,
45
+ }));
@@ -480,13 +480,13 @@ new cognito.UserPool(this, 'myuserpool', {
480
480
 
481
481
  ### Threat Protection
482
482
 
483
- This feature is only available if your Feature Plan is set to PLUS.
484
-
485
483
  Threat Protection can be set to configure enforcement levels and automatic responses for users in password-based and custom-challenge authentication flows.
486
484
  For configuration, there are 2 options for standard authentication and custom authentication.
487
485
  These are represented with properties `standardThreatProtectionMode` and `customThreatProtectionMode`.
488
486
  See the [documentation on Threat Protection](https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pool-settings-threat-protection.html)
489
487
 
488
+ **Note**: Threat Protection requires the PLUS feature plan for new user pools. CDK allows you to configure threat protection settings at synthesis time, and CloudFormation will validate feature plan requirements at deployment time. Existing user pools that are grandfathered on LITE plans with threat protection enabled will continue to work.
489
+
490
490
 
491
491
  ### Emails
492
492
 
@@ -872,6 +872,32 @@ table.addToResourcePolicy(new iam.PolicyStatement({
872
872
  TableV2 doesn’t support creating a replica and adding a resource-based policy to that replica in the same stack update in Regions other than the Region where you deploy the stack update.
873
873
  To incorporate a resource-based policy into a replica, you'll need to initially deploy the replica without the policy, followed by a subsequent update to include the desired policy.
874
874
 
875
+ ### Grant Methods and Resource Policies
876
+
877
+ Grant methods like `grantReadData()`, `grantWriteData()`, and `grantReadWriteData()` automatically add permissions to resource policies when used with same-account principals (like `AccountRootPrincipal`). This happens transparently:
878
+
879
+ ```ts
880
+ const table = new dynamodb.TableV2(this, 'Table', {
881
+ partitionKey: { name: 'pk', type: dynamodb.AttributeType.STRING },
882
+ });
883
+
884
+ // Automatically adds to table's resource policy (same account)
885
+ table.grantReadData(new iam.AccountRootPrincipal());
886
+
887
+ // Adds to IAM user's policy (not resource policy)
888
+ declare const user: iam.User;
889
+ table.grantReadData(user);
890
+ ```
891
+
892
+ **How it works:**
893
+ - **Same-account principals** (AccountRootPrincipal, AccountPrincipal): Grant adds statement to table's resource policy
894
+ - **IAM identities** (User, Role, Group): Grant adds statement to the identity's IAM policy
895
+ - **Resource policy statements**: Automatically use wildcard resources (`*`) to avoid circular dependencies
896
+
897
+ This behavior follows the same pattern as other AWS services like KMS and S3, where grants intelligently choose between resource policies and identity policies based on the principal type.
898
+
899
+ **To avoid wildcards in resource policies:** If you need scoped resource ARNs instead of wildcards, use `addToResourcePolicy()` directly with an explicit table name instead of grant methods. See the "Scoped Resource Policies (Advanced)" section above for details.
900
+
875
901
  ## Grants
876
902
 
877
903
  Using any of the `grant*` methods on an instance of the `TableV2` construct will only apply to the primary table, its indexes, and any associated `encryptionKey`. As an example, `grantReadData` used below will only apply the table in `us-west-2`:
@@ -26,6 +26,7 @@ import { IntegTest } from '@aws-cdk/integ-tests-alpha';
26
26
  export class TestStack extends Stack {
27
27
  public readonly wildcardTable: dynamodb.Table;
28
28
  public readonly scopedTable: dynamodb.Table;
29
+ public readonly grantTable: dynamodb.Table;
29
30
 
30
31
  constructor(scope: Construct, id: string, props?: StackProps) {
31
32
  super(scope, id, props);
@@ -66,6 +67,22 @@ export class TestStack extends Stack {
66
67
  // Use CloudFormation intrinsic function to construct table ARN with known table name
67
68
  resources: [Fn.sub('arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/my-explicit-scoped-table')],
68
69
  }));
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());
69
86
  }
70
87
  }
71
88
 
@@ -2,6 +2,7 @@ import * as ec2 from 'aws-cdk-lib/aws-ec2';
2
2
  import * as cdk from 'aws-cdk-lib';
3
3
  import { Construct } from 'constructs';
4
4
  import * as ecs from 'aws-cdk-lib/aws-ecs';
5
+ import * as integ from '@aws-cdk/integ-tests-alpha';
5
6
 
6
7
  const app = new cdk.App({
7
8
  postCliContext: {
@@ -12,24 +13,29 @@ const app = new cdk.App({
12
13
  },
13
14
  });
14
15
 
15
- class EcsStack extends cdk.Stack {
16
- constructor(scope: Construct, id: string, props?: cdk.StackProps) {
17
- super(scope, id, props);
18
-
16
+ class BaseEcsStack extends cdk.Stack {
17
+ protected createBaseResources() {
19
18
  const vpc = new ec2.Vpc(this, 'VPC', { restrictDefaultSecurityGroup: false });
20
-
21
19
  const cluster = new ecs.Cluster(this, 'EcsCluster', { vpc });
22
20
  cluster.addCapacity('DefaultAutoScalingGroup', {
23
21
  instanceType: ec2.InstanceType.of(ec2.InstanceClass.T2, ec2.InstanceSize.MICRO),
24
22
  });
25
-
26
23
  const taskDefinition = new ecs.Ec2TaskDefinition(this, 'TaskDef');
27
24
  taskDefinition.addContainer('web', {
28
25
  image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'),
29
26
  memoryLimitMiB: 256,
30
27
  });
28
+ return { vpc, cluster, taskDefinition };
29
+ }
30
+ }
31
+
32
+ // Test service with multiple placement strategies
33
+ class EcsWithStrategiesStack extends BaseEcsStack {
34
+ constructor(scope: Construct, id: string, props?: cdk.StackProps) {
35
+ super(scope, id, props);
36
+ const { cluster, taskDefinition } = this.createBaseResources();
31
37
 
32
- new ecs.Ec2Service(this, 'Test_Stack', {
38
+ new ecs.Ec2Service(this, 'Service', {
33
39
  cluster,
34
40
  taskDefinition,
35
41
  placementStrategies: [
@@ -40,6 +46,24 @@ class EcsStack extends cdk.Stack {
40
46
  }
41
47
  }
42
48
 
43
- new EcsStack(app, 'aws-cdk-ecs-integration-test-stack');
49
+ // Test service with empty placement strategies
50
+ class EcsWithEmptyStrategiesStack extends BaseEcsStack {
51
+ constructor(scope: Construct, id: string, props?: cdk.StackProps) {
52
+ super(scope, id, props);
53
+ const { cluster, taskDefinition } = this.createBaseResources();
54
+
55
+ new ecs.Ec2Service(this, 'Service', {
56
+ cluster,
57
+ taskDefinition,
58
+ placementStrategies: [],
59
+ });
60
+ }
61
+ }
62
+ new integ.IntegTest(app, 'LambdaTest', {
63
+ testCases: [
64
+ new EcsWithStrategiesStack(app, 'ecs-placement-strategies-with-strategies'),
65
+ new EcsWithEmptyStrategiesStack(app, 'ecs-placement-strategies-empty'),
66
+ ],
67
+ });
44
68
 
45
69
  app.synth();