konokenj.cdk-api-mcp-server 0.50.0__py3-none-any.whl → 0.52.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 (29) hide show
  1. cdk_api_mcp_server/__about__.py +1 -1
  2. cdk_api_mcp_server/resources/aws-cdk/constructs/@aws-cdk/aws-bedrock-agentcore-alpha/README.md +327 -5
  3. cdk_api_mcp_server/resources/aws-cdk/constructs/@aws-cdk/aws-msk-alpha/README.md +30 -0
  4. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-apigateway/README.md +9 -0
  5. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-apigateway/integ.spec-restapi.ts +1 -0
  6. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-apigatewayv2/README.md +93 -81
  7. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-apigatewayv2/integ.stage.ts +20 -4
  8. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-codepipeline-actions/integ.pipeline-elastic-beanstalk-deploy.ts +4 -1
  9. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-dynamodb/README.md +53 -0
  10. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-dynamodb/integ.dynamodb.add-to-resource-policy.ts +80 -0
  11. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-dynamodb/integ.dynamodb.policy.ts +21 -1
  12. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-ec2/integ.vpc-flow-logs.ts +4 -0
  13. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-elasticloadbalancingv2/README.md +34 -4
  14. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-elasticloadbalancingv2/integ.nlb.security-group.ts +70 -0
  15. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-events-targets/README.md +22 -0
  16. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-events-targets/integ.firehose-delivery-stream.ts +51 -0
  17. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-iam/integ.managed-policy.ts +9 -0
  18. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-iam/integ.policy.ts +9 -0
  19. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-kinesisfirehose/README.md +60 -3
  20. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-kinesisfirehose/integ.cloudwatch-logs-processors.ts +45 -0
  21. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-lambda/integ.runtime.fromasset.ts +19 -4
  22. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-lambda/integ.runtime.inlinecode.ts +7 -0
  23. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/cx-api/FEATURE_FLAGS.md +32 -13
  24. {konokenj_cdk_api_mcp_server-0.50.0.dist-info → konokenj_cdk_api_mcp_server-0.52.0.dist-info}/METADATA +2 -2
  25. {konokenj_cdk_api_mcp_server-0.50.0.dist-info → konokenj_cdk_api_mcp_server-0.52.0.dist-info}/RECORD +28 -25
  26. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-events-targets/integ.kinesis-firehose-stream.ts +0 -33
  27. {konokenj_cdk_api_mcp_server-0.50.0.dist-info → konokenj_cdk_api_mcp_server-0.52.0.dist-info}/WHEEL +0 -0
  28. {konokenj_cdk_api_mcp_server-0.50.0.dist-info → konokenj_cdk_api_mcp_server-0.52.0.dist-info}/entry_points.txt +0 -0
  29. {konokenj_cdk_api_mcp_server-0.50.0.dist-info → konokenj_cdk_api_mcp_server-0.52.0.dist-info}/licenses/LICENSE.txt +0 -0
@@ -5,7 +5,7 @@ import * as iam from 'aws-cdk-lib/aws-iam';
5
5
  import { IManagedPolicy, ManagedPolicyReference } from 'aws-cdk-lib/aws-iam';
6
6
  import * as s3 from 'aws-cdk-lib/aws-s3';
7
7
  import * as deploy from 'aws-cdk-lib/aws-s3-deployment';
8
- import { App, Fn, RemovalPolicy, Stack, UnscopedValidationError } from 'aws-cdk-lib';
8
+ import { App, Fn, RemovalPolicy, ResourceEnvironment, Stack, UnscopedValidationError } from 'aws-cdk-lib';
9
9
  import * as integ from '@aws-cdk/integ-tests-alpha';
10
10
  import * as cpactions from 'aws-cdk-lib/aws-codepipeline-actions';
11
11
  import { Node } from 'constructs';
@@ -56,6 +56,9 @@ function makePolicy(arn: string): IManagedPolicy {
56
56
  get node(): Node {
57
57
  throw new UnscopedValidationError('The result of fromAwsManagedPolicyName can not be used in this API');
58
58
  },
59
+ get env(): ResourceEnvironment {
60
+ throw new UnscopedValidationError('The result of fromAwsManagedPolicyName can not be used in this API');
61
+ },
59
62
  };
60
63
  }
61
64
 
@@ -816,6 +816,59 @@ Using `resourcePolicy` you can add a [resource policy](https://docs.aws.amazon.c
816
816
  });
817
817
  ```
818
818
 
819
+ ### Adding Resource Policy Statements Dynamically
820
+
821
+ You can also add resource policy statements to a table after it's created using the `addToResourcePolicy` method. Following the same pattern as KMS, resource policies use wildcard resources to avoid circular dependencies:
822
+
823
+ ```ts
824
+ const table = new dynamodb.TableV2(this, 'Table', {
825
+ partitionKey: { name: 'pk', type: dynamodb.AttributeType.STRING },
826
+ });
827
+
828
+ // Standard resource policy (recommended approach)
829
+ table.addToResourcePolicy(new iam.PolicyStatement({
830
+ actions: ['dynamodb:GetItem', 'dynamodb:PutItem', 'dynamodb:Query'],
831
+ principals: [new iam.AccountRootPrincipal()],
832
+ resources: ['*'], // Wildcard avoids circular dependency - same pattern as KMS
833
+ }));
834
+
835
+ // Allow specific service access
836
+ table.addToResourcePolicy(new iam.PolicyStatement({
837
+ actions: ['dynamodb:Query'],
838
+ principals: [new iam.ServicePrincipal('lambda.amazonaws.com')],
839
+ resources: ['*'],
840
+ }));
841
+ ```
842
+
843
+ #### Scoped Resource Policies (Advanced)
844
+
845
+ For scoped resource policies that reference specific table ARNs, you must specify an explicit table name:
846
+
847
+ ```ts
848
+ import { Fn } from 'aws-cdk-lib';
849
+
850
+ // Table with explicit name enables scoped resource policies
851
+ const table = new dynamodb.TableV2(this, 'Table', {
852
+ tableName: 'my-explicit-table-name', // Required for scoped resources
853
+ partitionKey: { name: 'pk', type: dynamodb.AttributeType.STRING },
854
+ });
855
+
856
+ // Now you can use scoped resources
857
+ table.addToResourcePolicy(new iam.PolicyStatement({
858
+ actions: ['dynamodb:GetItem'],
859
+ principals: [new iam.AccountRootPrincipal()],
860
+ resources: [
861
+ Fn.sub('arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/my-explicit-table-name'),
862
+ Fn.sub('arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/my-explicit-table-name/index/*'),
863
+ ],
864
+ }));
865
+ ```
866
+
867
+ **Important Limitations:**
868
+ - **Auto-generated table names**: Must use `resources: ['*']` to avoid circular dependencies
869
+ - **Explicit table names**: Enable scoped resources but lose CDK's automatic naming benefits
870
+ - **CloudFormation constraint**: Resource policies cannot reference the resource they're attached to during creation
871
+
819
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.
820
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.
821
874
 
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Integration test for DynamoDB Table.addToResourcePolicy() method
3
+ *
4
+ * This test validates the fix for issue #35062: "(aws-dynamodb): `addToResourcePolicy` has no effect"
5
+ *
6
+ * WHAT WE'RE TESTING:
7
+ * - The addToResourcePolicy() method was broken - it had "no effect" when called
8
+ * - Resource policies weren't being added to the CloudFormation template
9
+ * - This created a security gap where developers thought they were securing tables but policies weren't applied
10
+ *
11
+ * TEST VALIDATION:
12
+ * 1. Creates DynamoDB tables with different resource policy configurations
13
+ * 2. Tests both wildcard resources (for auto-generated names) and scoped resources (for explicit names)
14
+ * 3. Verifies policies get added to CloudFormation templates with correct structure
15
+ * 4. Ensures both patterns work without circular dependencies
16
+ *
17
+ * @see https://github.com/aws/aws-cdk/issues/35062
18
+ */
19
+
20
+ import { App, Fn, RemovalPolicy, Stack, StackProps } from 'aws-cdk-lib';
21
+ import { Construct } from 'constructs';
22
+ import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';
23
+ import * as iam from 'aws-cdk-lib/aws-iam';
24
+ import { IntegTest } from '@aws-cdk/integ-tests-alpha';
25
+
26
+ export class TestStack extends Stack {
27
+ public readonly wildcardTable: dynamodb.Table;
28
+ public readonly scopedTable: dynamodb.Table;
29
+
30
+ constructor(scope: Construct, id: string, props?: StackProps) {
31
+ super(scope, id, props);
32
+
33
+ // TEST 1: Table with wildcard resource policy (auto-generated name)
34
+ // This is the standard pattern to avoid circular dependencies
35
+ this.wildcardTable = new dynamodb.Table(this, 'WildcardTable', {
36
+ partitionKey: {
37
+ name: 'id',
38
+ type: dynamodb.AttributeType.STRING,
39
+ },
40
+ removalPolicy: RemovalPolicy.DESTROY,
41
+ });
42
+
43
+ // Add resource policy with wildcard resources
44
+ this.wildcardTable.addToResourcePolicy(new iam.PolicyStatement({
45
+ actions: ['dynamodb:GetItem', 'dynamodb:PutItem', 'dynamodb:Query'],
46
+ principals: [new iam.AccountRootPrincipal()],
47
+ resources: ['*'], // Use wildcard to avoid circular dependency - standard pattern for resource policies
48
+ }));
49
+
50
+ // TEST 2: Table with scoped resource policy (explicit table name)
51
+ // This demonstrates how to use scoped resources when table name is known at synthesis time
52
+ this.scopedTable = new dynamodb.Table(this, 'ScopedTable', {
53
+ tableName: 'my-explicit-scoped-table', // Explicit name enables scoped ARN construction
54
+ partitionKey: {
55
+ name: 'id',
56
+ type: dynamodb.AttributeType.STRING,
57
+ },
58
+ removalPolicy: RemovalPolicy.DESTROY,
59
+ });
60
+
61
+ // Add resource policy with properly scoped resource using explicit table name
62
+ // This works because table name is known at synthesis time (no circular dependency)
63
+ this.scopedTable.addToResourcePolicy(new iam.PolicyStatement({
64
+ actions: ['dynamodb:GetItem', 'dynamodb:Query'],
65
+ principals: [new iam.AccountRootPrincipal()],
66
+ // Use CloudFormation intrinsic function to construct table ARN with known table name
67
+ resources: [Fn.sub('arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/my-explicit-scoped-table')],
68
+ }));
69
+ }
70
+ }
71
+
72
+ // Test Setup
73
+ const app = new App();
74
+ const stack = new TestStack(app, 'add-to-resource-policy-test-stack');
75
+
76
+ // Integration Test Configuration
77
+ new IntegTest(app, 'add-to-resource-policy-integ-test', {
78
+ testCases: [stack],
79
+ });
80
+
@@ -38,7 +38,27 @@ export class TestStack extends Stack {
38
38
  removalPolicy: RemovalPolicy.DESTROY,
39
39
  });
40
40
 
41
- this.tableTwo.grantReadData(new iam.AccountPrincipal('123456789012'));
41
+ // IMPORTANT: Cross-account grants with auto-generated table names create circular dependencies
42
+ //
43
+ // WHY NOT this.tableTwo.grantReadData(new iam.AccountPrincipal('123456789012'))?
44
+ // - Cross-account principals cannot have policies attached to them
45
+ // - Grant falls back to adding a resource policy to the table
46
+ // - Resource policy tries to reference this.tableArn (the table's own ARN)
47
+ // - This creates a circular dependency: Table → ResourcePolicy → Table ARN → Table
48
+ // - CloudFormation fails with "Circular dependency between resources"
49
+ //
50
+ // SOLUTIONS:
51
+ // 1. Use addToResourcePolicy with wildcard resources (this approach)
52
+ // 2. Use explicit table names: tableName: 'my-table-name' (enables scoped resources)
53
+ // 3. Use same-account principals (grants go to principal policy, not resource policy)
54
+ //
55
+ this.tableTwo.addToResourcePolicy(new iam.PolicyStatement({
56
+ actions: ['dynamodb:*'],
57
+ // we need a valid account for cross-account principal otherwise it won't deploy
58
+ // this account is from fact-table.ts
59
+ principals: [new iam.AccountPrincipal('127311923021')],
60
+ resources: ['*'], // Wildcard avoids circular dependency - same pattern as KMS
61
+ }));
42
62
  }
43
63
  }
44
64
 
@@ -72,6 +72,10 @@ class TestStack extends Stack {
72
72
  destination: FlowLogDestination.toS3(),
73
73
  });
74
74
 
75
+ vpc.addFlowLog('FlowLogsCloudwatch', {
76
+ destination: FlowLogDestination.toCloudWatchLogs(),
77
+ });
78
+
75
79
  const bucket = new s3.Bucket(this, 'Bucket', {
76
80
  removalPolicy: RemovalPolicy.DESTROY,
77
81
  autoDeleteObjects: true,
@@ -338,17 +338,13 @@ Balancers:
338
338
  ```ts
339
339
  declare const vpc: ec2.Vpc;
340
340
  declare const asg: autoscaling.AutoScalingGroup;
341
- declare const sg1: ec2.ISecurityGroup;
342
- declare const sg2: ec2.ISecurityGroup;
343
341
 
344
342
  // Create the load balancer in a VPC. 'internetFacing' is 'false'
345
343
  // by default, which creates an internal load balancer.
346
344
  const lb = new elbv2.NetworkLoadBalancer(this, 'LB', {
347
345
  vpc,
348
346
  internetFacing: true,
349
- securityGroups: [sg1],
350
347
  });
351
- lb.addSecurityGroup(sg2);
352
348
 
353
349
  // Add a listener on a particular port.
354
350
  const listener = lb.addListener('Listener', {
@@ -362,6 +358,40 @@ listener.addTargets('AppFleet', {
362
358
  });
363
359
  ```
364
360
 
361
+ ### Security Groups for Network Load Balancer
362
+
363
+ By default, Network Load Balancers (NLB) have a security group associated with them.
364
+ This is controlled by the feature flag `@aws-cdk/aws-elasticloadbalancingv2:networkLoadBalancerWithSecurityGroupByDefault`.
365
+ When this flag is enabled (the default for new projects), a security group will be automatically created and attached to the NLB unless you explicitly provide your own security groups via the `securityGroups` property.
366
+
367
+ If you wish to create an NLB without any security groups, you can set the `disableSecurityGroups` property to `true`. When this property is set, no security group will be associated with the NLB, regardless of the feature flag.
368
+
369
+ ```ts
370
+ declare const vpc: ec2.IVpc;
371
+
372
+ const nlb = new elbv2.NetworkLoadBalancer(this, 'LB', {
373
+ vpc,
374
+ // To disable security groups for this NLB
375
+ disableSecurityGroups: true,
376
+ });
377
+ ```
378
+
379
+ If you want to use your own security groups, provide them via the `securityGroups` property:
380
+
381
+ ```ts
382
+ declare const vpc: ec2.IVpc;
383
+ declare const sg1: ec2.ISecurityGroup;
384
+ declare const sg2: ec2.ISecurityGroup;
385
+
386
+ const nlb = new elbv2.NetworkLoadBalancer(this, 'LB', {
387
+ vpc,
388
+ // Provide your own security groups
389
+ securityGroups: [sg1],
390
+ });
391
+ // Add another security group to the NLB
392
+ nlb.addSecurityGroup(sg2);
393
+ ```
394
+
365
395
  ### Enforce security group inbound rules on PrivateLink traffic for a Network Load Balancer
366
396
 
367
397
  You can indicate whether to evaluate inbound security group rules for traffic
@@ -0,0 +1,70 @@
1
+ import { ExpectedResult, IntegTest, Match } from '@aws-cdk/integ-tests-alpha';
2
+ import { Stack, aws_ec2 as ec2, aws_elasticloadbalancingv2 as elbv2, App } from 'aws-cdk-lib';
3
+ import { Construct } from 'constructs';
4
+
5
+ class TestStack extends Stack {
6
+ public readonly lbWithSg: elbv2.NetworkLoadBalancer;
7
+ public readonly lbWithSg2: elbv2.NetworkLoadBalancer;
8
+ public readonly lbWithoutSg: elbv2.NetworkLoadBalancer;
9
+
10
+ constructor(scope: Construct, id: string) {
11
+ super(scope, id);
12
+
13
+ const vpc = new ec2.Vpc(this, 'Stack', {
14
+ maxAzs: 1,
15
+ natGateways: 0,
16
+ });
17
+
18
+ this.lbWithSg = new elbv2.NetworkLoadBalancer(this, 'NlbWithSecurityGroup', {
19
+ vpc,
20
+ });
21
+ this.lbWithSg2 = new elbv2.NetworkLoadBalancer(this, 'NlbWithSecurityGroup2', {
22
+ vpc,
23
+ securityGroups: [new ec2.SecurityGroup(this, 'SecurityGroup', {
24
+ vpc,
25
+ allowAllOutbound: true,
26
+ })],
27
+ });
28
+ this.lbWithSg.connections.allowTo(this.lbWithSg2, ec2.Port.tcp(1229));
29
+ this.lbWithoutSg = new elbv2.NetworkLoadBalancer(this, 'NlbWithoutSecurityGroup', {
30
+ vpc,
31
+ disableSecurityGroups: true,
32
+ });
33
+ }
34
+ }
35
+
36
+ const app = new App({
37
+ postCliContext: {
38
+ '@aws-cdk/aws-elasticloadbalancingv2:networkLoadBalancerWithSecurityGroupByDefault': true,
39
+ },
40
+ });
41
+ const stack = new TestStack(app, 'NlbSecurityGroupStack');
42
+ const integ = new IntegTest(app, 'NlbSecurityGroupInteg', {
43
+ testCases: [stack],
44
+ });
45
+ integ.assertions.awsApiCall('elastic-load-balancing-v2', 'describeLoadBalancers', {
46
+ LoadBalancerArns: [
47
+ stack.lbWithSg.loadBalancerArn,
48
+ stack.lbWithSg2.loadBalancerArn,
49
+ stack.lbWithoutSg.loadBalancerArn,
50
+ ],
51
+ }).expect(ExpectedResult.objectLike({
52
+ LoadBalancers: [
53
+ Match.objectLike({
54
+ LoadBalancerArn: stack.lbWithSg.loadBalancerArn,
55
+ SecurityGroups: Match.arrayWith([
56
+ Match.stringLikeRegexp('sg-[0-9a-f]{8,17}'),
57
+ ]),
58
+ }),
59
+ Match.objectLike({
60
+ LoadBalancerArn: stack.lbWithSg2.loadBalancerArn,
61
+ SecurityGroups: Match.arrayWith([
62
+ Match.stringLikeRegexp('sg-[0-9a-f]{8,17}'),
63
+ ]),
64
+ }),
65
+ Match.objectLike({
66
+ LoadBalancerArn: stack.lbWithoutSg.loadBalancerArn,
67
+ SecurityGroups: undefined,
68
+ }),
69
+ ],
70
+ }));
@@ -19,6 +19,7 @@ Currently supported are:
19
19
  - [Invoke an API Destination](#invoke-an-api-destination)
20
20
  - [Invoke an AppSync GraphQL API](#invoke-an-appsync-graphql-api)
21
21
  - [Put an event on an EventBridge bus](#put-an-event-on-an-eventbridge-bus)
22
+ - [Put an event on a Firehose delivery stream](#put-an-event-on-a-firehose-delivery-stream)
22
23
  - [Run an ECS Task](#run-an-ecs-task)
23
24
  - [Tagging Tasks](#tagging-tasks)
24
25
  - [Launch type for ECS Task](#launch-type-for-ecs-task)
@@ -528,6 +529,27 @@ rule.addTarget(new targets.EventBus(
528
529
  ));
529
530
  ```
530
531
 
532
+ ## Put an event on a Firehose delivery stream
533
+
534
+ Use the `FirehoseDeliveryStream` target to put event to an Amazon Data Firehose delivery stream.
535
+
536
+ The code snippet below creates the scheduled event rule that put events to an Amazon Data Firehose delivery stream.
537
+
538
+ ```ts
539
+ import * as firehose from 'aws-cdk-lib/aws-kinesisfirehose';
540
+ import * as s3 from 'aws-cdk-lib/aws-s3';
541
+
542
+ declare const bucket: s3.Bucket;
543
+ const stream = new firehose.DeliveryStream(this, 'DeliveryStream', {
544
+ destination: new firehose.S3Bucket(bucket),
545
+ });
546
+
547
+ const rule = new events.Rule(this, 'Rule', {
548
+ schedule: events.Schedule.expression('rate(1 minute)'),
549
+ });
550
+ rule.addTarget(new targets.FirehoseDeliveryStream(stream));
551
+ ```
552
+
531
553
  ## Run an ECS Task
532
554
 
533
555
  Use the `EcsTask` target to run an ECS Task.
@@ -0,0 +1,51 @@
1
+ import * as events from 'aws-cdk-lib/aws-events';
2
+ import * as firehose from 'aws-cdk-lib/aws-kinesisfirehose';
3
+ import * as s3 from 'aws-cdk-lib/aws-s3';
4
+ import * as cdk from 'aws-cdk-lib';
5
+ import * as targets from 'aws-cdk-lib/aws-events-targets';
6
+ import { IntegTest, ExpectedResult, AwsApiCall } from '@aws-cdk/integ-tests-alpha';
7
+
8
+ // ---------------------------------
9
+ // Define a rule that triggers a put to a Firehose delivery stream every 1min.
10
+
11
+ const app = new cdk.App();
12
+
13
+ const stack = new cdk.Stack(app, 'aws-cdk-firehose-event-target');
14
+
15
+ const bucket = new s3.Bucket(stack, 'firehose-bucket', {
16
+ autoDeleteObjects: true,
17
+ removalPolicy: cdk.RemovalPolicy.DESTROY,
18
+ });
19
+ const deliveryStream = new firehose.DeliveryStream(stack, 'MyDeliveryStream', {
20
+ destination: new firehose.S3Bucket(bucket, {
21
+ bufferingInterval: cdk.Duration.seconds(30),
22
+ }),
23
+ });
24
+
25
+ const event = new events.Rule(stack, 'EveryMinute', {
26
+ schedule: events.Schedule.rate(cdk.Duration.minutes(1)),
27
+ });
28
+
29
+ event.addTarget(new targets.FirehoseDeliveryStream(deliveryStream));
30
+
31
+ const testCase = new IntegTest(app, 'firehose-event-target-integ', {
32
+ testCases: [stack],
33
+ });
34
+
35
+ const s3ApiCall = testCase.assertions.awsApiCall('S3', 'listObjectsV2', {
36
+ Bucket: bucket.bucketName,
37
+ MaxKeys: 1,
38
+ }).expect(ExpectedResult.objectLike({
39
+ KeyCount: 1,
40
+ })).waitForAssertions({
41
+ interval: cdk.Duration.seconds(30),
42
+ totalTimeout: cdk.Duration.minutes(10),
43
+ });
44
+
45
+ if (s3ApiCall instanceof AwsApiCall) {
46
+ s3ApiCall.waiterProvider?.addToRolePolicy({
47
+ Effect: 'Allow',
48
+ Action: ['s3:GetObject', 's3:ListBucket'],
49
+ Resource: ['*'],
50
+ });
51
+ }
@@ -1,6 +1,7 @@
1
1
  import { App, Stack } from 'aws-cdk-lib';
2
2
  import { IntegTest } from '@aws-cdk/integ-tests-alpha';
3
3
  import { AccountRootPrincipal, Grant, ManagedPolicy, PolicyStatement, Role, User } from 'aws-cdk-lib/aws-iam';
4
+ import * as lambda from 'aws-cdk-lib/aws-lambda';
4
5
 
5
6
  const app = new App();
6
7
 
@@ -34,6 +35,14 @@ policy.attachToRole(role);
34
35
  const importedRole = Role.fromRoleArn(stack, 'ImportedRole', role.roleArn);
35
36
  policy.attachToRole(importedRole);
36
37
 
38
+ // Can be passed to grantInvoke, see https://github.com/aws/aws-cdk/issues/32980
39
+ const func = new lambda.Function(stack, 'Function', {
40
+ runtime: lambda.Runtime.NODEJS_LATEST,
41
+ handler: 'index.handler',
42
+ code: lambda.Code.fromInline('export const handler = async () => null'),
43
+ });
44
+ func.grantInvoke(policy);
45
+
37
46
  new IntegTest(app, 'ManagedPolicyInteg', {
38
47
  testCases: [stack],
39
48
  });
@@ -1,6 +1,7 @@
1
1
  import { App, Stack } from 'aws-cdk-lib';
2
2
  import { IntegTest } from '@aws-cdk/integ-tests-alpha';
3
3
  import { AccountRootPrincipal, Grant, Policy, PolicyStatement, Role, User } from 'aws-cdk-lib/aws-iam';
4
+ import * as lambda from 'aws-cdk-lib/aws-lambda';
4
5
 
5
6
  const app = new App();
6
7
 
@@ -21,6 +22,14 @@ role.grantAssumeRole(user);
21
22
 
22
23
  Grant.addToPrincipal({ actions: ['iam:*'], resourceArns: [role.roleArn], grantee: policy2 });
23
24
 
25
+ // Can be passed to grantInvoke, see https://github.com/aws/aws-cdk/issues/32980
26
+ const func = new lambda.Function(stack, 'Function', {
27
+ runtime: lambda.Runtime.NODEJS_LATEST,
28
+ handler: 'index.handler',
29
+ code: lambda.Code.fromInline('export const handler = async () => null'),
30
+ });
31
+ func.grantInvoke(policy);
32
+
24
33
  new IntegTest(app, 'PolicyInteg', {
25
34
  testCases: [stack],
26
35
  });
@@ -483,8 +483,11 @@ Data can be transformed before being delivered to destinations. There are two ty
483
483
  data processing for delivery streams: record transformation with AWS Lambda, and record
484
484
  format conversion using a schema stored in an AWS Glue table. If both types of data
485
485
  processing are configured, then the Lambda transformation is performed first. By default,
486
- no data processing occurs. This construct library currently only supports data
487
- transformation with AWS Lambda. See [#15501](https://github.com/aws/aws-cdk/issues/15501)
486
+ no data processing occurs.
487
+
488
+ This construct library currently only supports data
489
+ transformation with AWS Lambda and some built-in data processors.
490
+ See [#15501](https://github.com/aws/aws-cdk/issues/15501)
488
491
  to track the status of adding support for record format conversion.
489
492
 
490
493
  ### Data transformation with AWS Lambda
@@ -520,7 +523,7 @@ const lambdaProcessor = new firehose.LambdaFunctionProcessor(lambdaFunction, {
520
523
  });
521
524
  declare const bucket: s3.Bucket;
522
525
  const s3Destination = new firehose.S3Bucket(bucket, {
523
- processor: lambdaProcessor,
526
+ processors: [lambdaProcessor],
524
527
  });
525
528
  new firehose.DeliveryStream(this, 'Delivery Stream', {
526
529
  destination: s3Destination,
@@ -532,6 +535,60 @@ new firehose.DeliveryStream(this, 'Delivery Stream', {
532
535
  See: [Data Transformation](https://docs.aws.amazon.com/firehose/latest/dev/data-transformation.html)
533
536
  in the *Amazon Data Firehose Developer Guide*.
534
537
 
538
+ ### Add a new line delimiter when delivering data to Amazon S3
539
+
540
+ You can specify the `AppendDelimiterToRecordProcessor` built-in processor to add a new line delimiter between records in objects that are delivered to Amazon S3. This can be helpful for parsing objects in Amazon S3.
541
+ For details, see [Use Amazon S3 bucket prefix to deliver data](https://docs.aws.amazon.com/firehose/latest/dev/dynamic-partitioning-s3bucketprefix.html).
542
+
543
+ ```ts
544
+ declare const bucket: s3.Bucket;
545
+ const s3Destination = new firehose.S3Bucket(bucket, {
546
+ processors: [
547
+ new firehose.AppendDelimiterToRecordProcessor(),
548
+ ],
549
+ });
550
+ new firehose.DeliveryStream(this, 'Delivery Stream', {
551
+ destination: s3Destination,
552
+ });
553
+ ```
554
+
555
+ ### Decompress and extract message of CloudWatch Logs
556
+
557
+ CloudWatch Logs events are sent to Firehose in compressed gzip format. If you want to deliver decompressed log events to Firehose destinations, you can use the `DecompressionProcessor` to automatically decompress CloudWatch Logs.
558
+ For details, see [Send CloudWatch Logs to Firehose](https://docs.aws.amazon.com/firehose/latest/dev/writing-with-cloudwatch-logs.html).
559
+
560
+ You may also needed to specify `AppendDelimiterToRecordProcessor`
561
+ because decompressed log events record has no trailing newline.
562
+
563
+ ```ts
564
+ declare const bucket: s3.Bucket;
565
+ const s3Destination = new firehose.S3Bucket(bucket, {
566
+ processors: [
567
+ new firehose.DecompressionProcessor(),
568
+ new firehose.AppendDelimiterToRecordProcessor(),
569
+ ],
570
+ });
571
+ new firehose.DeliveryStream(this, 'Delivery Stream', {
572
+ destination: s3Destination,
573
+ });
574
+ ```
575
+
576
+ When you enable decompression, you have the option to also enable message extraction. When using message extraction, Firehose filters out all metadata, such as owner, loggroup, logstream, and others from the decompressed CloudWatch Logs records and delivers only the content inside the message fields.
577
+
578
+ ```ts
579
+ declare const bucket: s3.Bucket;
580
+ const s3Destination = new firehose.S3Bucket(bucket, {
581
+ processors: [
582
+ new firehose.DecompressionProcessor(),
583
+ new firehose.CloudWatchLogProcessor({ dataMessageExtraction: true }),
584
+ ],
585
+ });
586
+ new firehose.DeliveryStream(this, 'Delivery Stream', {
587
+ destination: s3Destination,
588
+ });
589
+ ```
590
+
591
+
535
592
  ## Specifying an IAM role
536
593
 
537
594
  The DeliveryStream class automatically creates IAM service roles with all the minimum
@@ -0,0 +1,45 @@
1
+ #!/usr/bin/env node
2
+ import * as path from 'path';
3
+ import * as firehose from 'aws-cdk-lib/aws-kinesisfirehose';
4
+ import * as lambdanodejs from 'aws-cdk-lib/aws-lambda-nodejs';
5
+ import * as s3 from 'aws-cdk-lib/aws-s3';
6
+ import * as cdk from 'aws-cdk-lib';
7
+ import { IntegTest } from '@aws-cdk/integ-tests-alpha';
8
+
9
+ const app = new cdk.App();
10
+
11
+ const stack = new cdk.Stack(app, 'firehose-delivery-stream-cloudwatch-logs-processors');
12
+
13
+ const bucket = new s3.Bucket(stack, 'DestinationBucket', {
14
+ removalPolicy: cdk.RemovalPolicy.DESTROY,
15
+ autoDeleteObjects: true,
16
+ });
17
+
18
+ const dataProcessorFunction = new lambdanodejs.NodejsFunction(stack, 'DataProcessorFunction', {
19
+ entry: path.join(__dirname, 'lambda-data-processor.js'),
20
+ timeout: cdk.Duration.minutes(1),
21
+ });
22
+
23
+ new firehose.DeliveryStream(stack, 'DecompressCloudWatchLogsEntry', {
24
+ destination: new firehose.S3Bucket(bucket, {
25
+ processors: [
26
+ new firehose.DecompressionProcessor(),
27
+ new firehose.AppendDelimiterToRecordProcessor(),
28
+ new firehose.LambdaFunctionProcessor(dataProcessorFunction),
29
+ ],
30
+ }),
31
+ });
32
+
33
+ new firehose.DeliveryStream(stack, 'ExtractCloudWatchLogsEntry', {
34
+ destination: new firehose.S3Bucket(bucket, {
35
+ processors: [
36
+ new firehose.DecompressionProcessor(),
37
+ new firehose.CloudWatchLogProcessor({ dataMessageExtraction: true }),
38
+ new firehose.LambdaFunctionProcessor(dataProcessorFunction),
39
+ ],
40
+ }),
41
+ });
42
+
43
+ new IntegTest(app, 'integ-tests', {
44
+ testCases: [stack],
45
+ });
@@ -10,20 +10,35 @@ const app = new App({
10
10
  });
11
11
  const stack = new Stack(app, 'aws-cdk-lambda-runtime-fromasset');
12
12
 
13
- const lambdaFunction = new Function(stack, 'MyFunction', {
13
+ const lambdaFunctionJava21 = new Function(stack, 'MyFunctionJava21', {
14
14
  runtime: Runtime.JAVA_21,
15
15
  handler: 'com.mycompany.app.LambdaMethodHandler::handleRequest',
16
16
  code: Code.fromAsset(path.join(__dirname, 'my-app-1.0-SNAPSHOT.zip')),
17
17
  });
18
18
 
19
+ const lambdaFunctionJava25 = new Function(stack, 'MyFunctionJava25', {
20
+ runtime: Runtime.JAVA_25,
21
+ handler: 'com.mycompany.app.LambdaMethodHandler::handleRequest',
22
+ code: Code.fromAsset(path.join(__dirname, 'my-app-1.0-SNAPSHOT.zip')),
23
+ });
24
+
19
25
  const integTest = new integ.IntegTest(app, 'Integ', { testCases: [stack] });
20
26
 
21
- const invoke = integTest.assertions.invokeFunction({
22
- functionName: lambdaFunction.functionName,
27
+ const invokeJava21 = integTest.assertions.invokeFunction({
28
+ functionName: lambdaFunctionJava21.functionName,
29
+ payload: '123',
30
+ });
31
+
32
+ invokeJava21.expect(integ.ExpectedResult.objectLike({
33
+ Payload: '"123"',
34
+ }));
35
+
36
+ const invokeJava25 = integTest.assertions.invokeFunction({
37
+ functionName: lambdaFunctionJava25.functionName,
23
38
  payload: '123',
24
39
  });
25
40
 
26
- invoke.expect(integ.ExpectedResult.objectLike({
41
+ invokeJava25.expect(integ.ExpectedResult.objectLike({
27
42
  Payload: '"123"',
28
43
  }));
29
44
 
@@ -55,6 +55,13 @@ const python313 = new Function(stack, 'PYTHON_3_13', {
55
55
  });
56
56
  new CfnOutput(stack, 'PYTHON_3_13-functionName', { value: python313.functionName });
57
57
 
58
+ const python314 = new Function(stack, 'PYTHON_3_14', {
59
+ code: new InlineCode('def handler(event, context):\n return "success"'),
60
+ handler: 'index.handler',
61
+ runtime: Runtime.PYTHON_3_14,
62
+ });
63
+ new CfnOutput(stack, 'PYTHON_3_14-functionName', { value: python314.functionName });
64
+
58
65
  const node20xfn = new Function(stack, 'NODEJS_20_X', {
59
66
  code: new InlineCode('exports.handler = async function(event) { return "success" }'),
60
67
  handler: 'index.handler',