konokenj.cdk-api-mcp-server 0.28.0__py3-none-any.whl → 0.30.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 (45) hide show
  1. cdk_api_mcp_server/__about__.py +1 -1
  2. cdk_api_mcp_server/resources/aws-cdk/constructs/@aws-cdk/aws-amplify-alpha/README.md +71 -0
  3. cdk_api_mcp_server/resources/aws-cdk/constructs/@aws-cdk/integ-tests-alpha/README.md +1 -1
  4. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-apigatewayv2/integ.api-dualstack.ts +4 -3
  5. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-apigatewayv2/integ.api.ts +4 -2
  6. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-apigatewayv2/integ.stage.ts +20 -7
  7. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-apigatewayv2-authorizers/integ.iam.ts +38 -34
  8. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-apigatewayv2-integrations/integ.sqs.ts +71 -58
  9. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-backup/README.md +2 -0
  10. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-backup/integ.backup.ts +12 -1
  11. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-cloudfront/README.md +18 -1
  12. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-cloudwatch/README.md +32 -0
  13. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-cloudwatch/integ.alarm-and-dashboard.ts +1 -0
  14. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-cloudwatch/integ.math-alarm-and-dashboard.ts +4 -0
  15. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-codedeploy/integ.deployment-config.ts +4 -15
  16. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-codedeploy/integ.deployment-group.ts +218 -40
  17. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-codepipeline-actions/integ.pipeline-elastic-beanstalk-deploy.ts +1 -1
  18. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-ec2/README.md +13 -0
  19. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-ec2/integ.client-vpn-endpoint-client-route-enforcement.ts +68 -0
  20. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-eks/README.md +83 -83
  21. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-eks/integ.eks-al2023-nodegroup.ts +1 -2
  22. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-eks/integ.fargate-cluster.ts +1 -3
  23. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-eks/integ.helm-chart-logging.ts +55 -0
  24. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-kms/README.md +4 -3
  25. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-kms/integ.alias-from-alias-name.ts +26 -0
  26. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-lambda/README.md +15 -0
  27. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-lambda/integ.log-retention.ts +8 -2
  28. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-lambda-event-sources/README.md +68 -0
  29. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-lambda-event-sources/integ.kafka-schema-registry.ts +186 -0
  30. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-rds/README.md +23 -13
  31. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-rds/integ.cluster-snapshot.ts +3 -71
  32. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-rds/integ.instance-engine-lifecycle-support.ts +53 -0
  33. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-s3/README.md +16 -0
  34. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-s3-notifications/integ.bucket-notifications.ts +42 -80
  35. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-stepfunctions-tasks/integ.call-aws-service-cross-region-lambda.ts +97 -0
  36. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-stepfunctions-tasks/integ.start-job-run.ts +51 -106
  37. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/cx-api/FEATURE_FLAGS.md +21 -0
  38. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/cx-api/README.md +14 -0
  39. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/pipelines/README.md +7 -1
  40. cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/pipelines/integ.newpipeline-single-publisher.ts +53 -0
  41. {konokenj_cdk_api_mcp_server-0.28.0.dist-info → konokenj_cdk_api_mcp_server-0.30.0.dist-info}/METADATA +2 -2
  42. {konokenj_cdk_api_mcp_server-0.28.0.dist-info → konokenj_cdk_api_mcp_server-0.30.0.dist-info}/RECORD +45 -38
  43. {konokenj_cdk_api_mcp_server-0.28.0.dist-info → konokenj_cdk_api_mcp_server-0.30.0.dist-info}/WHEEL +0 -0
  44. {konokenj_cdk_api_mcp_server-0.28.0.dist-info → konokenj_cdk_api_mcp_server-0.30.0.dist-info}/entry_points.txt +0 -0
  45. {konokenj_cdk_api_mcp_server-0.28.0.dist-info → konokenj_cdk_api_mcp_server-0.30.0.dist-info}/licenses/LICENSE.txt +0 -0
@@ -4,7 +4,11 @@ import * as lambda from 'aws-cdk-lib/aws-lambda';
4
4
  import { IntegTest } from '@aws-cdk/integ-tests-alpha';
5
5
  import { STANDARD_NODEJS_RUNTIME } from '../../config';
6
6
 
7
- const app = new cdk.App();
7
+ const app = new cdk.App({
8
+ postCliContext: {
9
+ '@aws-cdk/aws-lambda:useCdkManagedLogGroup': false,
10
+ },
11
+ });
8
12
 
9
13
  const stack = new cdk.Stack(app, 'aws-cdk-lambda-log-retention');
10
14
 
@@ -13,6 +17,7 @@ new lambda.Function(stack, 'OneWeek', {
13
17
  handler: 'index.handler',
14
18
  runtime: STANDARD_NODEJS_RUNTIME,
15
19
  logRetention: logs.RetentionDays.ONE_WEEK,
20
+ logRemovalPolicy: cdk.RemovalPolicy.DESTROY,
16
21
  });
17
22
 
18
23
  new lambda.Function(stack, 'OneMonth', {
@@ -20,6 +25,7 @@ new lambda.Function(stack, 'OneMonth', {
20
25
  handler: 'index.handler',
21
26
  runtime: STANDARD_NODEJS_RUNTIME,
22
27
  logRetention: logs.RetentionDays.ONE_MONTH,
28
+ logRemovalPolicy: cdk.RemovalPolicy.DESTROY,
23
29
  });
24
30
 
25
31
  new lambda.Function(stack, 'OneYear', {
@@ -27,10 +33,10 @@ new lambda.Function(stack, 'OneYear', {
27
33
  handler: 'index.handler',
28
34
  runtime: STANDARD_NODEJS_RUNTIME,
29
35
  logRetention: logs.RetentionDays.ONE_YEAR,
36
+ logRemovalPolicy: cdk.RemovalPolicy.DESTROY,
30
37
  });
31
38
 
32
39
  new IntegTest(app, 'LambdaLogRetentionInteg', {
33
40
  testCases: [stack],
34
41
  diffAssets: true,
35
42
  });
36
- app.synth();
@@ -445,6 +445,74 @@ myFunction.addEventSource(new ManagedKafkaEventSource({
445
445
  }));
446
446
  ```
447
447
 
448
+ Set a confluent or self-managed schema registry to de-serialize events from the event source. Note, this will similarly work for `SelfManagedKafkaEventSource` but the example only shows setup for `ManagedKafkaEventSource`.
449
+
450
+ ```ts
451
+ import { ManagedKafkaEventSource, ConfluentSchemaRegistry } from 'aws-cdk-lib/aws-lambda-event-sources';
452
+ import { Secret } from 'aws-cdk-lib/aws-secretsmanager';
453
+
454
+ // Your MSK cluster arn
455
+ declare const clusterArn: string;
456
+
457
+ // The Kafka topic you want to subscribe to
458
+ const topic = 'some-cool-topic';
459
+
460
+ const secret = new Secret(this, 'Secret', { secretName: 'AmazonMSK_KafkaSecret' });
461
+
462
+ declare const myFunction: lambda.Function;
463
+ myFunction.addEventSource(new ManagedKafkaEventSource({
464
+ clusterArn,
465
+ topic,
466
+ startingPosition: lambda.StartingPosition.TRIM_HORIZON,
467
+ provisionedPollerConfig: {
468
+ minimumPollers: 1,
469
+ maximumPollers: 3,
470
+ },
471
+ schemaRegistryConfig: new ConfluentSchemaRegistry({
472
+ schemaRegistryUri: 'https://example.com',
473
+ eventRecordFormat: lambda.EventRecordFormat.JSON,
474
+ authenticationType: lambda.KafkaSchemaRegistryAccessConfigType.BASIC_AUTH,
475
+ secret: secret,
476
+ schemaValidationConfigs: [{ attribute: lambda.KafkaSchemaValidationAttribute.KEY }],
477
+ }),
478
+ }));
479
+ ```
480
+
481
+ Set Glue schema registry to de-serialize events from the event source. Note, this will similarly work for `SelfManagedKafkaEventSource` but the example only shows setup for `ManagedKafkaEventSource`.
482
+
483
+ ```ts
484
+ import { CfnRegistry } from 'aws-cdk-lib/aws-glue';
485
+ import { ManagedKafkaEventSource, GlueSchemaRegistry } from 'aws-cdk-lib/aws-lambda-event-sources';
486
+
487
+ // Your MSK cluster arn
488
+ declare const clusterArn: string;
489
+
490
+ // The Kafka topic you want to subscribe to
491
+ const topic = 'some-cool-topic';
492
+
493
+ // Your Glue Schema Registry
494
+ const glueRegistry = new CfnRegistry(this, 'Registry', {
495
+ name: 'schema-registry',
496
+ description: 'Schema registry for event source',
497
+ });
498
+
499
+ declare const myFunction: lambda.Function;
500
+ myFunction.addEventSource(new ManagedKafkaEventSource({
501
+ clusterArn,
502
+ topic,
503
+ startingPosition: lambda.StartingPosition.TRIM_HORIZON,
504
+ provisionedPollerConfig: {
505
+ minimumPollers: 1,
506
+ maximumPollers: 3,
507
+ },
508
+ schemaRegistryConfig: new GlueSchemaRegistry({
509
+ schemaRegistry: glueRegistry,
510
+ eventRecordFormat: lambda.EventRecordFormat.JSON,
511
+ schemaValidationConfigs: [{ attribute: lambda.KafkaSchemaValidationAttribute.KEY }],
512
+ }),
513
+ }));
514
+ ```
515
+
448
516
  ## Roadmap
449
517
 
450
518
  Eventually, this module will support all the event sources described under
@@ -0,0 +1,186 @@
1
+ import { TestFunction } from './test-function';
2
+ import { IntegTest } from '@aws-cdk/integ-tests-alpha';
3
+ import {
4
+ SelfManagedKafkaEventSource,
5
+ AuthenticationMethod,
6
+ GlueSchemaRegistry,
7
+ ConfluentSchemaRegistry,
8
+ } from 'aws-cdk-lib/aws-lambda-event-sources';
9
+ import * as lambda from 'aws-cdk-lib/aws-lambda';
10
+ import { App, StackProps, Stack, SecretValue } from 'aws-cdk-lib';
11
+ import * as secretsmanager from 'aws-cdk-lib/aws-secretsmanager';
12
+ import { CfnRegistry } from 'aws-cdk-lib/aws-glue';
13
+
14
+ // Self-Managed Kafka Stack with Schema Registry
15
+ export class SmkGlueSchemaRegistryStack extends Stack {
16
+ constructor(scope: App, id: string, props?: StackProps) {
17
+ super(scope, id, props);
18
+
19
+ // Create a Lambda function
20
+ const testLambdaFunction = new TestFunction(this, 'GlueFunction');
21
+
22
+ // Create dummy certificates for authentication
23
+ const dummyCertString = `-----BEGIN CERTIFICATE-----
24
+ MIIE5DCCAsygAwIBAgIRAPJdwaFaNRrytHBto0j5BA0wDQYJKoZIhvcNAQELBQAw
25
+ cmUuiAii9R0=
26
+ -----END CERTIFICATE-----
27
+ -----BEGIN CERTIFICATE-----
28
+ MIIFgjCCA2qgAwIBAgIQdjNZd6uFf9hbNC5RdfmHrzANBgkqhkiG9w0BAQsFADBb
29
+ c8PH3PSoAaRwMMgOSA2ALJvbRz8mpg==
30
+ -----END CERTIFICATE-----"
31
+ `;
32
+
33
+ const dummyPrivateKey = `-----BEGIN ENCRYPTED PRIVATE KEY-----
34
+ zp2mwJn2NYB7AZ7+imp0azDZb+8YG2aUCiyqb6PnnA==
35
+ -----END ENCRYPTED PRIVATE KEY-----`;
36
+
37
+ // Create secrets for Kafka authentication
38
+ const rootCASecret = new secretsmanager.Secret(this, 'GlueRootCASecret', {
39
+ secretObjectValue: {
40
+ certificate: SecretValue.unsafePlainText(dummyCertString),
41
+ },
42
+ });
43
+
44
+ const clientCertificatesSecret = new secretsmanager.Secret(this, 'GlueClientCertSecret', {
45
+ secretObjectValue: {
46
+ certificate: SecretValue.unsafePlainText(dummyCertString),
47
+ privateKey: SecretValue.unsafePlainText(dummyPrivateKey),
48
+ },
49
+ });
50
+
51
+ // Grant read permissions to the Lambda function
52
+ rootCASecret.grantRead(testLambdaFunction);
53
+ clientCertificatesSecret.grantRead(testLambdaFunction);
54
+
55
+ // Create a Glue Schema Registry
56
+ const glueRegistry = new CfnRegistry(this, 'SchemaRegistry', {
57
+ name: 'smk-glue-test-schema-registry',
58
+ description: 'Schema registry for SMK integration tests',
59
+ });
60
+
61
+ // Define Kafka bootstrap servers
62
+ const bootstrapServers = [
63
+ 'kafka-broker-1:9092',
64
+ 'kafka-broker-2:9092',
65
+ 'kafka-broker-3:9092',
66
+ ];
67
+
68
+ // Common configuration for SMK event sources
69
+ const commonConfig = {
70
+ bootstrapServers,
71
+ secret: clientCertificatesSecret,
72
+ authenticationMethod: AuthenticationMethod.CLIENT_CERTIFICATE_TLS_AUTH,
73
+ rootCACertificate: rootCASecret,
74
+ startingPosition: lambda.StartingPosition.TRIM_HORIZON,
75
+ };
76
+
77
+ // SMK with Glue Schema Registry
78
+ testLambdaFunction.addEventSource(new SelfManagedKafkaEventSource({
79
+ ...commonConfig,
80
+ topic: 'test-topic-smk-glue',
81
+ consumerGroupId: 'test-consumer-group-smk-glue',
82
+ provisionedPollerConfig: {
83
+ minimumPollers: 1,
84
+ maximumPollers: 3,
85
+ },
86
+ schemaRegistryConfig: new GlueSchemaRegistry({
87
+ schemaRegistry: glueRegistry,
88
+ eventRecordFormat: lambda.EventRecordFormat.JSON,
89
+ schemaValidationConfigs: [{ attribute: lambda.KafkaSchemaValidationAttribute.KEY }],
90
+ }),
91
+ }));
92
+ }
93
+ }
94
+
95
+ // Self-Managed Kafka Stack with Schema Registry
96
+ export class SmkConfluentSchemaRegistryStack extends Stack {
97
+ constructor(scope: App, id: string, props?: StackProps) {
98
+ super(scope, id, props);
99
+
100
+ // Create a Lambda function
101
+ const testLambdaFunction = new TestFunction(this, 'ConfluentFunction');
102
+
103
+ // Create dummy certificates for authentication
104
+ const dummyCertString = `-----BEGIN CERTIFICATE-----
105
+ MIIE5DCCAsygAwIBAgIRAPJdwaFaNRrytHBto0j5BA0wDQYJKoZIhvcNAQELBQAw
106
+ cmUuiAii9R0=
107
+ -----END CERTIFICATE-----
108
+ -----BEGIN CERTIFICATE-----
109
+ MIIFgjCCA2qgAwIBAgIQdjNZd6uFf9hbNC5RdfmHrzANBgkqhkiG9w0BAQsFADBb
110
+ c8PH3PSoAaRwMMgOSA2ALJvbRz8mpg==
111
+ -----END CERTIFICATE-----"
112
+ `;
113
+
114
+ const dummyPrivateKey = `-----BEGIN ENCRYPTED PRIVATE KEY-----
115
+ zp2mwJn2NYB7AZ7+imp0azDZb+8YG2aUCiyqb6PnnA==
116
+ -----END ENCRYPTED PRIVATE KEY-----`;
117
+
118
+ // Create secrets for Kafka authentication
119
+ const rootCASecret = new secretsmanager.Secret(this, 'ConfluentRootCASecret', {
120
+ secretObjectValue: {
121
+ certificate: SecretValue.unsafePlainText(dummyCertString),
122
+ },
123
+ });
124
+
125
+ const clientCertificatesSecret = new secretsmanager.Secret(this, 'ConfluentClientCertSecret', {
126
+ secretObjectValue: {
127
+ certificate: SecretValue.unsafePlainText(dummyCertString),
128
+ privateKey: SecretValue.unsafePlainText(dummyPrivateKey),
129
+ },
130
+ });
131
+
132
+ // Grant read permissions to the Lambda function
133
+ rootCASecret.grantRead(testLambdaFunction);
134
+ clientCertificatesSecret.grantRead(testLambdaFunction);
135
+
136
+ // Define Kafka bootstrap servers
137
+ const bootstrapServers = [
138
+ 'kafka-broker-1:9092',
139
+ 'kafka-broker-2:9092',
140
+ 'kafka-broker-3:9092',
141
+ ];
142
+
143
+ // Common configuration for SMK event sources
144
+ const commonConfig = {
145
+ bootstrapServers,
146
+ secret: clientCertificatesSecret,
147
+ authenticationMethod: AuthenticationMethod.CLIENT_CERTIFICATE_TLS_AUTH,
148
+ rootCACertificate: rootCASecret,
149
+ startingPosition: lambda.StartingPosition.TRIM_HORIZON,
150
+ };
151
+
152
+ // SMK with Confluent Schema Registry
153
+ testLambdaFunction.addEventSource(new SelfManagedKafkaEventSource({
154
+ ...commonConfig,
155
+ topic: 'test-topic-smk-confluent',
156
+ consumerGroupId: 'test-consumer-group-smk-confluent',
157
+ provisionedPollerConfig: {
158
+ minimumPollers: 1,
159
+ maximumPollers: 3,
160
+ },
161
+ schemaRegistryConfig: new ConfluentSchemaRegistry({
162
+ schemaRegistryUri: 'https://schema-registry.example.com',
163
+ eventRecordFormat: lambda.EventRecordFormat.JSON,
164
+ authenticationType: lambda.KafkaSchemaRegistryAccessConfigType.BASIC_AUTH,
165
+ secret: clientCertificatesSecret,
166
+ schemaValidationConfigs: [{ attribute: lambda.KafkaSchemaValidationAttribute.KEY }],
167
+ }),
168
+ }));
169
+ }
170
+ }
171
+
172
+ // Create the app and stacks
173
+ const app = new App({
174
+ postCliContext: {
175
+ '@aws-cdk/aws-lambda:createNewPoliciesWithAddToRolePolicy': false,
176
+ },
177
+ });
178
+ const glueStack = new SmkGlueSchemaRegistryStack(app, 'lambda-event-source-glue-schema-registry');
179
+ const confluentStack = new SmkConfluentSchemaRegistryStack(app, 'lambda-event-source-confluent-schema-registry');
180
+
181
+ // Create the integration tests
182
+ new IntegTest(app, 'SchemaRegistryInteg', {
183
+ testCases: [glueStack, confluentStack],
184
+ });
185
+
186
+ app.synth();
@@ -147,19 +147,6 @@ new rds.DatabaseCluster(this, 'DatabaseCluster', {
147
147
  });
148
148
  ```
149
149
 
150
- To configure [the life cycle type of the cluster](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/extended-support.html), use the `engineLifecycleSupport` property:
151
-
152
- ```ts
153
- declare const vpc: ec2.IVpc;
154
-
155
- new rds.DatabaseCluster(this, 'DatabaseCluster', {
156
- engine: rds.DatabaseClusterEngine.auroraMysql({ version: rds.AuroraMysqlEngineVersion.VER_3_07_0 }),
157
- writer: rds.ClusterInstance.serverlessV2('writerInstance'),
158
- vpc,
159
- engineLifecycleSupport: rds.EngineLifecycleSupport.OPEN_SOURCE_RDS_EXTENDED_SUPPORT,
160
- });
161
- ```
162
-
163
150
  ### Updating the database instances in a cluster
164
151
 
165
152
  Database cluster instances may be updated in bulk or on a rolling basis.
@@ -1576,6 +1563,29 @@ new rds.DatabaseCluster(this, 'Cluster', {
1576
1563
  });
1577
1564
  ```
1578
1565
 
1566
+ ## Extended Support
1567
+
1568
+ With Amazon RDS Extended Support, you can continue running your database on a major engine version past the RDS end of
1569
+ standard support date for an additional cost. To configure the life cycle type, use the `engineLifecycleSupport` property:
1570
+
1571
+ ```ts
1572
+ declare const vpc: ec2.IVpc;
1573
+
1574
+ new rds.DatabaseCluster(this, 'DatabaseCluster', {
1575
+ engine: rds.DatabaseClusterEngine.auroraMysql({ version: rds.AuroraMysqlEngineVersion.VER_3_07_0 }),
1576
+ writer: rds.ClusterInstance.serverlessV2('writerInstance'),
1577
+ vpc,
1578
+ engineLifecycleSupport: rds.EngineLifecycleSupport.OPEN_SOURCE_RDS_EXTENDED_SUPPORT,
1579
+ });
1580
+
1581
+ new rds.DatabaseInstance(this, 'DatabaseInstance', {
1582
+ engine: rds.DatabaseInstanceEngine.mysql({ version: rds.MysqlEngineVersion.VER_8_0_39 }),
1583
+ instanceType: ec2.InstanceType.of(ec2.InstanceClass.R7G, ec2.InstanceSize.LARGE),
1584
+ vpc,
1585
+ engineLifecycleSupport: rds.EngineLifecycleSupport.OPEN_SOURCE_RDS_EXTENDED_SUPPORT_DISABLED,
1586
+ });
1587
+ ```
1588
+
1579
1589
  ## Importing existing DatabaseInstance
1580
1590
 
1581
1591
  ### Lookup DatabaseInstance by instanceIdentifier
@@ -1,14 +1,10 @@
1
- import * as path from 'path';
2
1
  import * as ec2 from 'aws-cdk-lib/aws-ec2';
3
- import * as iam from 'aws-cdk-lib/aws-iam';
4
- import * as lambda from 'aws-cdk-lib/aws-lambda';
5
- import { App, ArnFormat, CustomResource, RemovalPolicy, Stack, StackProps } from 'aws-cdk-lib';
6
- import * as cr from 'aws-cdk-lib/custom-resources';
2
+ import { App, RemovalPolicy, Stack, StackProps } from 'aws-cdk-lib';
7
3
  import { Construct } from 'constructs';
8
4
  import * as rds from 'aws-cdk-lib/aws-rds';
9
5
  import { ClusterInstance } from 'aws-cdk-lib/aws-rds';
10
6
  import { IntegTest } from '@aws-cdk/integ-tests-alpha';
11
- import { STANDARD_NODEJS_RUNTIME } from '../../config';
7
+ import { ClusterSnapshoter } from './snapshoter';
12
8
 
13
9
  class TestStack extends Stack {
14
10
  constructor(scope: Construct, id: string, props?: StackProps) {
@@ -34,7 +30,7 @@ class TestStack extends Stack {
34
30
  removalPolicy: RemovalPolicy.DESTROY,
35
31
  });
36
32
 
37
- const snapshoter = new Snapshoter(this, 'Snapshoter', {
33
+ const snapshoter = new ClusterSnapshoter(this, 'Snapshoter', {
38
34
  cluster,
39
35
  snapshotIdentifier: 'cdk-integ-cluster-snapshot',
40
36
  });
@@ -58,69 +54,6 @@ class TestStack extends Stack {
58
54
  }
59
55
  }
60
56
 
61
- interface SnapshoterProps {
62
- readonly cluster: rds.IDatabaseCluster;
63
- readonly snapshotIdentifier: string;
64
- }
65
-
66
- class Snapshoter extends Construct {
67
- public readonly snapshotArn: string;
68
-
69
- constructor(scope: Construct, id: string, props: SnapshoterProps) {
70
- super(scope, id);
71
-
72
- const clusterArn = Stack.of(this).formatArn({
73
- service: 'rds',
74
- resource: 'cluster',
75
- resourceName: props.cluster.clusterIdentifier,
76
- arnFormat: ArnFormat.COLON_RESOURCE_NAME,
77
- });
78
-
79
- const snapshotArn = Stack.of(this).formatArn({
80
- service: 'rds',
81
- resource: 'cluster-snapshot',
82
- resourceName: props.snapshotIdentifier,
83
- arnFormat: ArnFormat.COLON_RESOURCE_NAME,
84
- });
85
-
86
- const code = lambda.Code.fromAsset(path.join(__dirname, 'snapshot-handler'), { exclude: ['*.ts'] });
87
- const onEventHandler = new lambda.Function(this, 'OnEventHandler', {
88
- code,
89
- runtime: STANDARD_NODEJS_RUNTIME,
90
- handler: 'index.onEventHandler',
91
- });
92
- onEventHandler.addToRolePolicy(new iam.PolicyStatement({
93
- actions: ['rds:CreateDBClusterSnapshot', 'rds:DeleteDBClusterSnapshot'],
94
- resources: [clusterArn, snapshotArn],
95
- }));
96
-
97
- const isCompleteHandler = new lambda.Function(this, 'IsCompleteHandler', {
98
- code,
99
- runtime: STANDARD_NODEJS_RUNTIME,
100
- handler: 'index.isCompleteHandler',
101
- });
102
- isCompleteHandler.addToRolePolicy(new iam.PolicyStatement({
103
- actions: ['rds:DescribeDBClusterSnapshots'],
104
- resources: [clusterArn, snapshotArn],
105
- }));
106
-
107
- const provider = new cr.Provider(this, 'SnapshotProvider', {
108
- onEventHandler,
109
- isCompleteHandler,
110
- });
111
-
112
- const customResource = new CustomResource(this, 'Snapshot', {
113
- resourceType: 'Custom::Snapshoter',
114
- serviceToken: provider.serviceToken,
115
- properties: {
116
- DBClusterIdentifier: props.cluster.clusterIdentifier,
117
- DBClusterSnapshotIdentifier: props.snapshotIdentifier,
118
- },
119
- });
120
- this.snapshotArn = customResource.getAttString('DBClusterSnapshotArn');
121
- }
122
- }
123
-
124
57
  const app = new App({
125
58
  postCliContext: {
126
59
  '@aws-cdk/aws-lambda:useCdkManagedLogGroup': false,
@@ -133,4 +66,3 @@ new IntegTest(app, 'ClusterSnapshotInteg', {
133
66
  testCases: [stack],
134
67
  diffAssets: true,
135
68
  });
136
- app.synth();
@@ -0,0 +1,53 @@
1
+ import * as ec2 from 'aws-cdk-lib/aws-ec2';
2
+ import { App, RemovalPolicy, Stack } from 'aws-cdk-lib';
3
+ import * as rds from 'aws-cdk-lib/aws-rds';
4
+ import { IntegTest } from '@aws-cdk/integ-tests-alpha';
5
+ import { InstanceSnapshoter } from './snapshoter';
6
+
7
+ const app = new App({
8
+ postCliContext: {
9
+ '@aws-cdk/aws-lambda:createNewPoliciesWithAddToRolePolicy': true,
10
+ '@aws-cdk/aws-lambda:useCdkManagedLogGroup': false,
11
+ },
12
+ });
13
+
14
+ const stack = new Stack(app, 'cdk-instance-engine-lifecycle-support');
15
+
16
+ const vpc = new ec2.Vpc(stack, 'Vpc', { maxAzs: 2, natGateways: 1, restrictDefaultSecurityGroup: false });
17
+
18
+ const engine = rds.DatabaseInstanceEngine.mysql({ version: rds.MysqlEngineVersion.VER_8_4_5 });
19
+ const instanceType = ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE3, ec2.InstanceSize.SMALL);
20
+
21
+ const sourceInstance = new rds.DatabaseInstance(stack, 'Instance', {
22
+ engine,
23
+ instanceType,
24
+ vpc,
25
+ removalPolicy: RemovalPolicy.DESTROY,
26
+ engineLifecycleSupport: rds.EngineLifecycleSupport.OPEN_SOURCE_RDS_EXTENDED_SUPPORT,
27
+ });
28
+
29
+ const snapshoter = new InstanceSnapshoter(stack, 'Snapshoter', {
30
+ instance: sourceInstance,
31
+ snapshotIdentifier: 'cdk-instance-engine-lifecycle-support-snapshot',
32
+ });
33
+
34
+ const restoredInstance = new rds.DatabaseInstanceFromSnapshot(stack, 'FromSnapshot', {
35
+ snapshotIdentifier: snapshoter.snapshotArn,
36
+ engine,
37
+ instanceType,
38
+ vpc,
39
+ removalPolicy: RemovalPolicy.DESTROY,
40
+ engineLifecycleSupport: rds.EngineLifecycleSupport.OPEN_SOURCE_RDS_EXTENDED_SUPPORT_DISABLED,
41
+ });
42
+
43
+ new rds.DatabaseInstanceReadReplica(stack, 'ReadReplica', {
44
+ sourceDatabaseInstance: restoredInstance,
45
+ instanceType,
46
+ vpc,
47
+ removalPolicy: RemovalPolicy.DESTROY,
48
+ engineLifecycleSupport: rds.EngineLifecycleSupport.OPEN_SOURCE_RDS_EXTENDED_SUPPORT_DISABLED,
49
+ });
50
+
51
+ new IntegTest(app, 'cdk-instance-engine-lifecycle-support-test', {
52
+ testCases: [stack],
53
+ });
@@ -156,6 +156,22 @@ bucket.grantReadWrite(myLambda);
156
156
  Will give the Lambda's execution role permissions to read and write
157
157
  from the bucket.
158
158
 
159
+ ### Understanding "grant" Methods
160
+
161
+ The S3 construct library provides several grant methods for the `Bucket` resource, but two of them have a special behavior. This two accept an `objectsKeyPattern` parameter to restrict granted permissions to specific resources:
162
+ - `grantRead`
163
+ - `grantReadWrite`
164
+
165
+ When examining the synthesized policy, you'll notice it includes both your specified object key patterns and the bucket itself.
166
+ This is by design. Some permissions (like `s3:ListBucket`) apply at the bucket level, while others (like `s3:GetObject`) apply to specific objects.
167
+
168
+ Specifically, the [`s3:ListBucket` action operates on bucket resources](https://docs.aws.amazon.com/service-authorization/latest/reference/list_amazons3.html#amazons3-bucket)
169
+ and requires the bucket ARN to work properly. This might be seen as a bug, giving the impression that more permissions were granted than the ones you intended, but the reality is that the policy does not ignore your `objectsKeyPattern` - object-specific actions like `s3:GetObject`
170
+ will still be limited to the resources defined in your pattern.
171
+
172
+ If you need to restrict the `s3:ListBucket` action to specific paths, you can add a `Condition` to your policy that limits the `objectsKeyPattern` to specific folders. For more details and examples, see the [AWS documentation on bucket policies](https://docs.aws.amazon.com/AmazonS3/latest/userguide/example-bucket-policies.html#example-bucket-policies-folders).
173
+
174
+
159
175
  ## AWS Foundational Security Best Practices
160
176
 
161
177
  ### Enforcing SSL
@@ -1,106 +1,68 @@
1
- import * as lambda from 'aws-cdk-lib/aws-lambda';
2
1
  import * as s3 from 'aws-cdk-lib/aws-s3';
2
+ import * as sqs from 'aws-cdk-lib/aws-sqs';
3
3
  import * as cdk from 'aws-cdk-lib';
4
- import * as s3n from 'aws-cdk-lib/aws-s3-notifications';
5
- import { STANDARD_NODEJS_RUNTIME } from '../../../config';
6
- import * as constructs from 'constructs';
7
4
  import * as integ from '@aws-cdk/integ-tests-alpha';
5
+ import * as s3n from 'aws-cdk-lib/aws-s3-notifications';
8
6
 
9
7
  const app = new cdk.App({
10
8
  postCliContext: {
11
- '@aws-cdk/aws-lambda:useCdkManagedLogGroup': false,
12
9
  '@aws-cdk/aws-s3:keepNotificationInImportedBucket': false,
13
10
  },
14
11
  });
15
12
 
16
- const stack = new cdk.Stack(app, 'cdk-integ-lambda-bucket-s3-notifications');
13
+ const stack = new cdk.Stack(app, 'sqs-bucket-notifications');
17
14
 
18
- const bucketA = new s3.Bucket(stack, 'MyBucket', {
15
+ const bucket1 = new s3.Bucket(stack, 'Bucket1', {
19
16
  removalPolicy: cdk.RemovalPolicy.DESTROY,
20
17
  });
18
+ const queue = new sqs.Queue(stack, 'MyQueue');
21
19
 
22
- const fn = new lambda.Function(stack, 'MyFunction', {
23
- runtime: STANDARD_NODEJS_RUNTIME,
24
- handler: 'index.handler',
25
- code: lambda.Code.fromInline(`exports.handler = ${handler.toString()}`),
26
- });
20
+ bucket1.addObjectCreatedNotification(new s3n.SqsDestination(queue));
27
21
 
28
- const bucketB = new s3.Bucket(stack, 'YourBucket', {
22
+ const bucket2 = new s3.Bucket(stack, 'Bucket2', {
29
23
  removalPolicy: cdk.RemovalPolicy.DESTROY,
24
+ autoDeleteObjects: true,
30
25
  });
26
+ bucket2.addObjectCreatedNotification(new s3n.SqsDestination(queue), { suffix: '.png' });
31
27
 
32
- bucketB.addEventNotification(s3.EventType.OBJECT_REMOVED, new s3n.LambdaDestination(fn));
33
-
34
- const c1 = new constructs.Construct(stack, 'Construct1');
35
- const unmanagedBucket = s3.Bucket.fromBucketName(c1, 'IntegUnmanagedBucket1', bucketA.bucketName);
36
-
37
- unmanagedBucket.addObjectCreatedNotification(new s3n.LambdaDestination(fn), { prefix: 'TEST1/', suffix: '.png' });
38
- unmanagedBucket.addEventNotification(s3.EventType.OBJECT_CREATED, new s3n.LambdaDestination(fn), { prefix: 'TEST2/' });
28
+ const encryptedQueue = new sqs.Queue(stack, 'EncryptedQueue', { encryption: sqs.QueueEncryption.KMS });
29
+ bucket1.addObjectRemovedNotification(new s3n.SqsDestination(encryptedQueue));
39
30
 
40
- /* eslint-disable no-console */
41
- function handler(event: any, _context: any, callback: any) {
42
- console.log(JSON.stringify(event, undefined, 2));
43
- return callback(null, event);
44
- }
31
+ const bucket3 = new s3.Bucket(stack, 'Bucket3WithSkipDestinationValidation', {
32
+ notificationsSkipDestinationValidation: true,
33
+ removalPolicy: cdk.RemovalPolicy.DESTROY,
34
+ });
35
+ const queueWithIncorrectS3Permissions = new sqs.Queue(stack, 'MyQueueWithIncorrectS3Permissions');
36
+ queueWithIncorrectS3Permissions.addToResourcePolicy(
37
+ new cdk.aws_iam.PolicyStatement({
38
+ effect: cdk.aws_iam.Effect.DENY,
39
+ actions: ['sqs:SendMessage'],
40
+ principals: [new cdk.aws_iam.ServicePrincipal('s3.amazonaws.com')],
41
+ resources: [queueWithIncorrectS3Permissions.queueArn],
42
+ }));
43
+ bucket3.addEventNotification(s3.EventType.OBJECT_TAGGING_PUT, new s3n.SqsDestination(queueWithIncorrectS3Permissions));
45
44
 
46
- const integTest = new integ.IntegTest(app, 'LambdaBucketNotificationsTest', {
47
- cdkCommandOptions: {
48
- deploy: {
49
- args: {
50
- rollback: false,
51
- },
52
- },
53
- },
45
+ const integTest = new integ.IntegTest(app, 'SQSBucketNotificationsTest', {
54
46
  testCases: [stack],
55
47
  diffAssets: true,
56
48
  });
57
49
 
58
- const getNotifications = integTest.assertions
59
- .awsApiCall('S3', 'getBucketNotificationConfiguration', {
60
- Bucket: unmanagedBucket.bucketName,
61
- });
62
- getNotifications.provider.addToRolePolicy({
63
- Effect: 'Allow',
64
- Action: ['s3:GetBucketNotification'],
65
- Resource: ['*'],
66
- });
50
+ integTest.assertions
51
+ // First remove the test notifications
52
+ .awsApiCall('SQS', 'purgeQueue', {
53
+ QueueUrl: queue.queueUrl,
54
+ })
55
+ .next(integTest.assertions
56
+ .awsApiCall('S3', 'putObject', {
57
+ Bucket: bucket2.bucketName,
58
+ Key: 'image.png',
59
+ Body: 'Some content',
60
+ }))
61
+ .next(integTest.assertions
62
+ .awsApiCall('SQS', 'receiveMessage', {
63
+ QueueUrl: queue.queueUrl,
64
+ WaitTimeSeconds: 20,
65
+ })
66
+ .assertAtPath('Messages.0.Body.Records.0.s3.object.key', integ.ExpectedResult.stringLikeRegexp('image\\.png')));
67
67
 
68
- getNotifications.expect(integ.ExpectedResult.objectLike({
69
- LambdaFunctionConfigurations: [
70
- {
71
- Events: [
72
- 's3:ObjectCreated:*',
73
- ],
74
- Filter: {
75
- Key: {
76
- FilterRules: [
77
- {
78
- Name: 'Prefix',
79
- Value: 'TEST1/',
80
- },
81
- {
82
- Name: 'Suffix',
83
- Value: '.png',
84
- },
85
- ],
86
- },
87
- },
88
- },
89
- {
90
- Events: [
91
- 's3:ObjectCreated:*',
92
- ],
93
- Filter: {
94
- Key: {
95
- FilterRules: [
96
- {
97
- Name: 'Prefix',
98
- Value: 'TEST2/',
99
- },
100
- ],
101
- },
102
- },
103
- },
104
- ],
105
- }));
106
68
  app.synth();